From b6c9110de28592d3eacb6b6745aa226bdd80e369 Mon Sep 17 00:00:00 2001 From: Ted Goldman Date: Thu, 9 Apr 2020 22:19:00 -0400 Subject: [PATCH 001/736] format for upstream --- src/vs/editor/common/config/editorOptions.ts | 11 +++++++++++ src/vs/editor/contrib/find/findModel.ts | 2 +- src/vs/monaco.d.ts | 4 ++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 03354b46bce..fd731e97daf 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -1236,6 +1236,10 @@ class EditorEmptySelectionClipboard extends EditorBooleanOption constructor() { const defaults: EditorFindOptions = { + moveOnType: true, seedSearchStringFromSelection: true, autoFindInSelection: 'never', globalFindClipboard: false, @@ -1274,6 +1279,11 @@ class EditorFind extends BaseEditorOption super( EditorOption.find, 'find', defaults, { + 'editor.find.moveOnType': { + type: 'boolean', + default: defaults.moveOnType, + description: nls.localize('find.moveOnType', "Controls whether the cursor should move to find matches while typing.") + }, 'editor.find.seedSearchStringFromSelection': { type: 'boolean', default: defaults.seedSearchStringFromSelection, @@ -1317,6 +1327,7 @@ class EditorFind extends BaseEditorOption } const input = _input as IEditorFindOptions; return { + moveOnType: EditorBooleanOption.boolean(input.moveOnType, this.defaultValue.moveOnType), seedSearchStringFromSelection: EditorBooleanOption.boolean(input.seedSearchStringFromSelection, this.defaultValue.seedSearchStringFromSelection), autoFindInSelection: typeof _input.autoFindInSelection === 'boolean' ? (_input.autoFindInSelection ? 'always' : 'never') diff --git a/src/vs/editor/contrib/find/findModel.ts b/src/vs/editor/contrib/find/findModel.ts index c083478dd00..a0816803467 100644 --- a/src/vs/editor/contrib/find/findModel.ts +++ b/src/vs/editor/contrib/find/findModel.ts @@ -195,7 +195,7 @@ export class FindModelBoundToEditorModel { undefined ); - if (moveCursor) { + if (moveCursor && this._editor.getOption(EditorOption.find).moveOnType) { this._moveToNextMatch(this._decorations.getStartPosition()); } } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 99d3a926bea..693f2b2a3f0 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3266,6 +3266,10 @@ declare namespace monaco.editor { * Configuration options for editor find widget */ export interface IEditorFindOptions { + /** + * Controls whether the cursor should move to find matches while typing. + */ + moveOnType?: boolean; /** * Controls if we seed search string in the Find Widget with editor selection. */ From 91c1a43f07ec4e42bde51545fc0f40c6684aef04 Mon Sep 17 00:00:00 2001 From: Ted Goldman Date: Fri, 10 Apr 2020 20:45:39 -0400 Subject: [PATCH 002/736] debounced Find Widget, preventing cursor movement until typing is finished --- src/vs/editor/common/config/editorOptions.ts | 2 +- src/vs/editor/contrib/find/findModel.ts | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index fd731e97daf..4c4d1401569 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -1282,7 +1282,7 @@ class EditorFind extends BaseEditorOption 'editor.find.moveOnType': { type: 'boolean', default: defaults.moveOnType, - description: nls.localize('find.moveOnType', "Controls whether the cursor should move to find matches while typing.") + description: nls.localize('find.moveOnType', "Controls whether the cursor should jump to find matches while typing.") }, 'editor.find.seedSearchStringFromSelection': { type: 'boolean', diff --git a/src/vs/editor/contrib/find/findModel.ts b/src/vs/editor/contrib/find/findModel.ts index a0816803467..bc80cedf4f2 100644 --- a/src/vs/editor/contrib/find/findModel.ts +++ b/src/vs/editor/contrib/find/findModel.ts @@ -69,6 +69,9 @@ export const FIND_IDS = { export const MATCHES_LIMIT = 19999; const RESEARCH_DELAY = 240; +let debounceTimer: ReturnType; +let DEBOUNCE_DURATION = 240; + export class FindModelBoundToEditorModel { private readonly _editor: IActiveCodeEditor; @@ -168,7 +171,7 @@ export class FindModelBoundToEditorModel { return model.getFullModelRange(); } - private research(moveCursor: boolean, newFindScope?: Range | null): void { + private research (moveCursor: boolean, newFindScope?: Range | null): void { let findScope: Range | null = null; if (typeof newFindScope !== 'undefined') { findScope = newFindScope; @@ -196,7 +199,10 @@ export class FindModelBoundToEditorModel { ); if (moveCursor && this._editor.getOption(EditorOption.find).moveOnType) { - this._moveToNextMatch(this._decorations.getStartPosition()); + clearTimeout(debounceTimer); + debounceTimer = setTimeout(() => { + this._moveToNextMatch(this._decorations.getStartPosition()); + }, DEBOUNCE_DURATION); } } From b9efdab4831b6b69c13e4b15b55d519c65d0e2d4 Mon Sep 17 00:00:00 2001 From: Jody Heavener Date: Sat, 30 May 2020 00:57:56 -0400 Subject: [PATCH 003/736] refactor find widget to support multiple selection find/replace --- src/vs/editor/common/model.ts | 4 +- src/vs/editor/common/model/textModel.ts | 26 ++++-- src/vs/editor/contrib/find/findController.ts | 28 ++++--- src/vs/editor/contrib/find/findDecorations.ts | 41 ++++++---- src/vs/editor/contrib/find/findModel.ts | 61 ++++++++------ src/vs/editor/contrib/find/findState.ts | 12 ++- src/vs/editor/contrib/find/findWidget.ts | 47 +++++++---- .../contrib/find/test/findController.test.ts | 24 +++--- .../contrib/find/test/findModel.test.ts | 81 ++++++++++++++++++- .../editor/contrib/multicursor/multicursor.ts | 14 ++-- src/vs/monaco.d.ts | 4 +- 11 files changed, 246 insertions(+), 96 deletions(-) diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index 122ef954003..5e4661bc8f4 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -800,7 +800,7 @@ export interface ITextModel { /** * Search the model. * @param searchString The string used to search. If it is a regular expression, set `isRegex` to true. - * @param searchScope Limit the searching to only search inside this range. + * @param searchScope Limit the searching to only search inside these ranges. * @param isRegex Used to indicate that `searchString` is a regular expression. * @param matchCase Force the matching to match lower/upper case exactly. * @param wordSeparators Force the matching to match entire words only. Pass null otherwise. @@ -808,7 +808,7 @@ export interface ITextModel { * @param limitResultCount Limit the number of results * @return The ranges where the matches are. It is empty if no matches have been found. */ - findMatches(searchString: string, searchScope: IRange, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean, limitResultCount?: number): FindMatch[]; + findMatches(searchString: string, searchScope: IRange | IRange[], isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean, limitResultCount?: number): FindMatch[]; /** * Search the model for the next match. Loops to the beginning of the model if needed. * @param searchString The string used to search. If it is a regular expression, set `isRegex` to true. diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 7e48e64a271..b9fb2d21f28 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -1112,13 +1112,23 @@ export class TextModel extends Disposable implements model.ITextModel { public findMatches(searchString: string, rawSearchScope: any, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean, limitResultCount: number = LIMIT_FIND_COUNT): model.FindMatch[] { this._assertNotDisposed(); - let searchRange: Range; - if (Range.isIRange(rawSearchScope)) { - searchRange = this.validateRange(rawSearchScope); - } else { - searchRange = this.getFullModelRange(); + let searchRanges: Range[] | null = null; + + if (rawSearchScope !== null) { + if (!Array.isArray(rawSearchScope)) { + rawSearchScope = [rawSearchScope]; + } + + if (rawSearchScope.every((searchScope: Range) => Range.isIRange(searchScope))) { + searchRanges = rawSearchScope.map((searchScope: Range) => this.validateRange(searchScope)); + } } + if (searchRanges === null) { + searchRanges = [this.getFullModelRange()]; + } + + let matchMapper: (value: Range, index: number, array: Range[]) => model.FindMatch[]; if (!isRegex && searchString.indexOf('\n') < 0) { // not regex, not multi line const searchParams = new SearchParams(searchString, isRegex, matchCase, wordSeparators); @@ -1128,10 +1138,12 @@ export class TextModel extends Disposable implements model.ITextModel { return []; } - return this.findMatchesLineByLine(searchRange, searchData, captureMatches, limitResultCount); + matchMapper = (searchRange: Range) => this.findMatchesLineByLine(searchRange, searchData, captureMatches, limitResultCount); + } else { + matchMapper = (searchRange: Range) => TextModelSearch.findMatches(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchRange, captureMatches, limitResultCount); } - return TextModelSearch.findMatches(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchRange, captureMatches, limitResultCount); + return searchRanges.map(matchMapper).reduce((arr, matches: model.FindMatch[]) => arr.concat(matches), []); } public findNextMatch(searchString: string, rawSearchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string, captureMatches: boolean): model.FindMatch | null { diff --git a/src/vs/editor/contrib/find/findController.ts b/src/vs/editor/contrib/find/findController.ts index 8d2f6668924..62a132c823c 100644 --- a/src/vs/editor/contrib/find/findController.ts +++ b/src/vs/editor/contrib/find/findController.ts @@ -233,12 +233,22 @@ export class CommonFindController extends Disposable implements IEditorContribut this._state.change({ searchScope: null }, true); } else { if (this._editor.hasModel()) { - let selection = this._editor.getSelection(); - if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) { - selection = selection.setEndPosition(selection.endLineNumber - 1, this._editor.getModel().getLineMaxColumn(selection.endLineNumber - 1)); - } - if (!selection.isEmpty()) { - this._state.change({ searchScope: selection }, true); + let selections = this._editor.getSelections(); + selections.map(selection => { + if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) { + selection = selection.setEndPosition( + selection.endLineNumber - 1, + this._editor.getModel()!.getLineMaxColumn(selection.endLineNumber - 1) + ); + } + if (!selection.isEmpty()) { + return selection; + } + return null; + }).filter(element => !!element); + + if (selections.length) { + this._state.change({ searchScope: selections }, true); } } } @@ -293,9 +303,9 @@ export class CommonFindController extends Disposable implements IEditorContribut } if (opts.updateSearchScope) { - let currentSelection = this._editor.getSelection(); - if (!currentSelection.isEmpty()) { - stateChanges.searchScope = currentSelection; + let currentSelections = this._editor.getSelections(); + if (currentSelections.some(selection => !selection.isEmpty())) { + stateChanges.searchScope = currentSelections; } } diff --git a/src/vs/editor/contrib/find/findDecorations.ts b/src/vs/editor/contrib/find/findDecorations.ts index a3f137d1575..c6b966e77c1 100644 --- a/src/vs/editor/contrib/find/findDecorations.ts +++ b/src/vs/editor/contrib/find/findDecorations.ts @@ -17,7 +17,7 @@ export class FindDecorations implements IDisposable { private readonly _editor: IActiveCodeEditor; private _decorations: string[]; private _overviewRulerApproximateDecorations: string[]; - private _findScopeDecorationId: string | null; + private _findScopeDecorationIds: string[]; private _rangeHighlightDecorationId: string | null; private _highlightedDecorationId: string | null; private _startPosition: Position; @@ -26,7 +26,7 @@ export class FindDecorations implements IDisposable { this._editor = editor; this._decorations = []; this._overviewRulerApproximateDecorations = []; - this._findScopeDecorationId = null; + this._findScopeDecorationIds = []; this._rangeHighlightDecorationId = null; this._highlightedDecorationId = null; this._startPosition = this._editor.getPosition(); @@ -37,7 +37,7 @@ export class FindDecorations implements IDisposable { this._decorations = []; this._overviewRulerApproximateDecorations = []; - this._findScopeDecorationId = null; + this._findScopeDecorationIds = []; this._rangeHighlightDecorationId = null; this._highlightedDecorationId = null; } @@ -45,7 +45,7 @@ export class FindDecorations implements IDisposable { public reset(): void { this._decorations = []; this._overviewRulerApproximateDecorations = []; - this._findScopeDecorationId = null; + this._findScopeDecorationIds = []; this._rangeHighlightDecorationId = null; this._highlightedDecorationId = null; } @@ -54,9 +54,22 @@ export class FindDecorations implements IDisposable { return this._decorations.length; } + /** @deprecated use getFindScopes to support multiple selections */ public getFindScope(): Range | null { - if (this._findScopeDecorationId) { - return this._editor.getModel().getDecorationRange(this._findScopeDecorationId); + if (this._findScopeDecorationIds[0]) { + return this._editor.getModel().getDecorationRange(this._findScopeDecorationIds[0]); + } + return null; + } + + public getFindScopes(): Range[] | null { + if (this._findScopeDecorationIds.length) { + const scopes = this._findScopeDecorationIds.map(findScopeDecorationId => + this._editor.getModel().getDecorationRange(findScopeDecorationId) + ).filter(element => !!element); + if (scopes.length) { + return scopes as Range[]; + } } return null; } @@ -133,7 +146,7 @@ export class FindDecorations implements IDisposable { return matchPosition; } - public set(findMatches: FindMatch[], findScope: Range | null): void { + public set(findMatches: FindMatch[], findScopes: Range[] | null): void { this._editor.changeDecorations((accessor) => { let findMatchesOptions: ModelDecorationOptions = FindDecorations._FIND_MATCH_DECORATION; @@ -195,12 +208,12 @@ export class FindDecorations implements IDisposable { } // Find scope - if (this._findScopeDecorationId) { - accessor.removeDecoration(this._findScopeDecorationId); - this._findScopeDecorationId = null; + if (this._findScopeDecorationIds.length) { + this._findScopeDecorationIds.forEach(findScopeDecorationId => accessor.removeDecoration(findScopeDecorationId)); + this._findScopeDecorationIds = []; } - if (findScope) { - this._findScopeDecorationId = accessor.addDecoration(findScope, FindDecorations._FIND_SCOPE_DECORATION); + if (findScopes?.length) { + this._findScopeDecorationIds = findScopes.map(findScope => accessor.addDecoration(findScope, FindDecorations._FIND_SCOPE_DECORATION)); } }); } @@ -253,8 +266,8 @@ export class FindDecorations implements IDisposable { let result: string[] = []; result = result.concat(this._decorations); result = result.concat(this._overviewRulerApproximateDecorations); - if (this._findScopeDecorationId) { - result.push(this._findScopeDecorationId); + if (this._findScopeDecorationIds.length) { + result.push(...this._findScopeDecorationIds); } if (this._rangeHighlightDecorationId) { result.push(this._rangeHighlightDecorationId); diff --git a/src/vs/editor/contrib/find/findModel.ts b/src/vs/editor/contrib/find/findModel.ts index c083478dd00..38efb9dc70b 100644 --- a/src/vs/editor/contrib/find/findModel.ts +++ b/src/vs/editor/contrib/find/findModel.ts @@ -168,26 +168,36 @@ export class FindModelBoundToEditorModel { return model.getFullModelRange(); } - private research(moveCursor: boolean, newFindScope?: Range | null): void { - let findScope: Range | null = null; + private research(moveCursor: boolean, newFindScope?: Range | Range[] | null): void { + let findScopes: Range[] | null = null; if (typeof newFindScope !== 'undefined') { - findScope = newFindScope; - } else { - findScope = this._decorations.getFindScope(); - } - if (findScope !== null) { - if (findScope.startLineNumber !== findScope.endLineNumber) { - if (findScope.endColumn === 1) { - findScope = new Range(findScope.startLineNumber, 1, findScope.endLineNumber - 1, this._editor.getModel().getLineMaxColumn(findScope.endLineNumber - 1)); + if (newFindScope !== null) { + if (!Array.isArray(newFindScope)) { + findScopes = [newFindScope as Range]; } else { - // multiline find scope => expand to line starts / ends - findScope = new Range(findScope.startLineNumber, 1, findScope.endLineNumber, this._editor.getModel().getLineMaxColumn(findScope.endLineNumber)); + findScopes = newFindScope; } } + } else { + findScopes = this._decorations.getFindScopes(); + } + if (findScopes !== null) { + findScopes = findScopes.map(findScope => { + if (findScope.startLineNumber !== findScope.endLineNumber) { + let endLineNumber = findScope.endLineNumber; + + if (findScope.endColumn === 1) { + endLineNumber = endLineNumber - 1; + } + + return new Range(findScope.startLineNumber, 1, endLineNumber, this._editor.getModel().getLineMaxColumn(endLineNumber)); + } + return findScope; + }); } - let findMatches = this._findMatches(findScope, false, MATCHES_LIMIT); - this._decorations.set(findMatches, findScope); + let findMatches = this._findMatches(findScopes, false, MATCHES_LIMIT); + this._decorations.set(findMatches, findScopes); this._state.changeMatchInfo( this._decorations.getCurrentMatchesPosition(this._editor.getSelection()), @@ -443,9 +453,12 @@ export class FindModelBoundToEditorModel { } } - private _findMatches(findScope: Range | null, captureMatches: boolean, limitResultCount: number): FindMatch[] { - let searchRange = FindModelBoundToEditorModel._getSearchRange(this._editor.getModel(), findScope); - return this._editor.getModel().findMatches(this._state.searchString, searchRange, this._state.isRegex, this._state.matchCase, this._state.wholeWord ? this._editor.getOption(EditorOption.wordSeparators) : null, captureMatches, limitResultCount); + private _findMatches(findScopes: Range[] | null, captureMatches: boolean, limitResultCount: number): FindMatch[] { + const searchRanges = (findScopes as [] || [null]).map((scope: Range | null) => + FindModelBoundToEditorModel._getSearchRange(this._editor.getModel(), scope) + ); + + return this._editor.getModel().findMatches(this._state.searchString, searchRanges, this._state.isRegex, this._state.matchCase, this._state.wholeWord ? this._editor.getOption(EditorOption.wordSeparators) : null, captureMatches, limitResultCount); } public replaceAll(): void { @@ -453,13 +466,13 @@ export class FindModelBoundToEditorModel { return; } - const findScope = this._decorations.getFindScope(); + const findScopes = this._decorations.getFindScopes(); - if (findScope === null && this._state.matchesCount >= MATCHES_LIMIT) { + if (findScopes === null && this._state.matchesCount >= MATCHES_LIMIT) { // Doing a replace on the entire file that is over ${MATCHES_LIMIT} matches this._largeReplaceAll(); } else { - this._regularReplaceAll(findScope); + this._regularReplaceAll(findScopes); } this.research(false); @@ -504,10 +517,10 @@ export class FindModelBoundToEditorModel { this._executeEditorCommand('replaceAll', command); } - private _regularReplaceAll(findScope: Range | null): void { + private _regularReplaceAll(findScopes: Range[] | null): void { const replacePattern = this._getReplacePattern(); // Get all the ranges (even more than the highlighted ones) - let matches = this._findMatches(findScope, replacePattern.hasReplacementPatterns || this._state.preserveCase, Constants.MAX_SAFE_SMALL_INTEGER); + let matches = this._findMatches(findScopes, replacePattern.hasReplacementPatterns || this._state.preserveCase, Constants.MAX_SAFE_SMALL_INTEGER); let replaceStrings: string[] = []; for (let i = 0, len = matches.length; i < len; i++) { @@ -523,10 +536,10 @@ export class FindModelBoundToEditorModel { return; } - let findScope = this._decorations.getFindScope(); + let findScopes = this._decorations.getFindScopes(); // Get all the ranges (even more than the highlighted ones) - let matches = this._findMatches(findScope, false, Constants.MAX_SAFE_SMALL_INTEGER); + let matches = this._findMatches(findScopes, false, Constants.MAX_SAFE_SMALL_INTEGER); let selections = matches.map(m => new Selection(m.range.startLineNumber, m.range.startColumn, m.range.endLineNumber, m.range.endColumn)); // If one of the ranges is the editor selection, then maintain it as primary diff --git a/src/vs/editor/contrib/find/findState.ts b/src/vs/editor/contrib/find/findState.ts index dbadf2bb664..0313dd8bc46 100644 --- a/src/vs/editor/contrib/find/findState.ts +++ b/src/vs/editor/contrib/find/findState.ts @@ -46,7 +46,7 @@ export interface INewFindReplaceState { matchCaseOverride?: FindOptionOverride; preserveCase?: boolean; preserveCaseOverride?: FindOptionOverride; - searchScope?: Range | null; + searchScope?: Range[] | null; loop?: boolean; } @@ -73,7 +73,7 @@ export class FindReplaceState extends Disposable { private _matchCaseOverride: FindOptionOverride; private _preserveCase: boolean; private _preserveCaseOverride: FindOptionOverride; - private _searchScope: Range | null; + private _searchScope: Range[] | null; private _matchesPosition: number; private _matchesCount: number; private _currentMatch: Range | null; @@ -94,7 +94,7 @@ export class FindReplaceState extends Disposable { public get actualMatchCase(): boolean { return this._matchCase; } public get actualPreserveCase(): boolean { return this._preserveCase; } - public get searchScope(): Range | null { return this._searchScope; } + public get searchScope(): Range[] | null { return this._searchScope; } public get matchesPosition(): number { return this._matchesPosition; } public get matchesCount(): number { return this._matchesCount; } public get currentMatch(): Range | null { return this._currentMatch; } @@ -238,7 +238,11 @@ export class FindReplaceState extends Disposable { this._preserveCase = newState.preserveCase; } if (typeof newState.searchScope !== 'undefined') { - if (!Range.equalsRange(this._searchScope, newState.searchScope)) { + if (!newState.searchScope?.every((newSearchScope) => { + return this._searchScope?.some(existingSearchScope => { + return !Range.equalsRange(existingSearchScope, newSearchScope); + }); + })) { this._searchScope = newState.searchScope; changeEvent.searchScope = true; somethingChanged = true; diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index 6a1c15acbea..8dce48f614f 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -803,16 +803,26 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL } if (this._toggleSelectionFind.checked) { - let selection = this._codeEditor.getSelection(); - if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) { - selection = selection.setEndPosition(selection.endLineNumber - 1, this._codeEditor.getModel().getLineMaxColumn(selection.endLineNumber - 1)); - } - const currentMatch = this._state.currentMatch; - if (selection.startLineNumber !== selection.endLineNumber) { - if (!Range.equalsRange(selection, currentMatch)) { - // Reseed find scope - this._state.change({ searchScope: selection }, true); + let selections = this._codeEditor.getSelections(); + + selections.map(selection => { + if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) { + selection = selection.setEndPosition( + selection.endLineNumber - 1, + this._codeEditor.getModel()!.getLineMaxColumn(selection.endLineNumber - 1) + ); } + const currentMatch = this._state.currentMatch; + if (selection.startLineNumber !== selection.endLineNumber) { + if (!Range.equalsRange(selection, currentMatch)) { + return selection; + } + } + return null; + }).filter(element => !!element); + + if (selections.length) { + this._state.change({ searchScope: selections as Range[] }, true); } } } @@ -1027,12 +1037,19 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL this._register(this._toggleSelectionFind.onChange(() => { if (this._toggleSelectionFind.checked) { if (this._codeEditor.hasModel()) { - let selection = this._codeEditor.getSelection(); - if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) { - selection = selection.setEndPosition(selection.endLineNumber - 1, this._codeEditor.getModel().getLineMaxColumn(selection.endLineNumber - 1)); - } - if (!selection.isEmpty()) { - this._state.change({ searchScope: selection }, true); + let selections = this._codeEditor.getSelections(); + selections.map(selection => { + if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) { + selection = selection.setEndPosition(selection.endLineNumber - 1, this._codeEditor.getModel()!.getLineMaxColumn(selection.endLineNumber - 1)); + } + if (!selection.isEmpty()) { + return selection; + } + return null; + }).filter(element => !!element); + + if (selections.length) { + this._state.change({ searchScope: selections as Range[] }, true); } } } else { diff --git a/src/vs/editor/contrib/find/test/findController.test.ts b/src/vs/editor/contrib/find/test/findController.test.ts index 44895a2d658..b440d42671a 100644 --- a/src/vs/editor/contrib/find/test/findController.test.ts +++ b/src/vs/editor/contrib/find/test/findController.test.ts @@ -309,10 +309,10 @@ suite('FindController', () => { assert.equal(findController.getState().searchScope, null); findController.getState().change({ - searchScope: new Range(1, 1, 1, 5) + searchScope: [new Range(1, 1, 1, 5)] }, false); - assert.deepEqual(findController.getState().searchScope, new Range(1, 1, 1, 5)); + assert.deepEqual(findController.getState().searchScope, [new Range(1, 1, 1, 5)]); findController.closeFindWidget(); assert.equal(findController.getState().searchScope, null); @@ -523,10 +523,8 @@ suite('FindController query options persistence', () => { 'var z = (3 * 5)', ], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, (editor) => { // clipboardState = ''; - editor.setSelection(new Range(1, 1, 2, 1)); let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); - - findController.start({ + const findConfig = { forceRevealReplace: false, seedSearchStringFromSelection: false, seedSearchStringFromGlobalClipboard: false, @@ -534,9 +532,17 @@ suite('FindController query options persistence', () => { shouldAnimate: false, updateSearchScope: true, loop: true - }); + }; - assert.deepEqual(findController.getState().searchScope, new Selection(1, 1, 2, 1)); + editor.setSelection(new Range(1, 1, 2, 1)); + findController.start(findConfig); + assert.deepEqual(findController.getState().searchScope, [new Selection(1, 1, 2, 1)]); + + findController.closeFindWidget(); + + editor.setSelections([new Selection(1, 1, 2, 1), new Selection(2, 1, 2, 5)]); + findController.start(findConfig); + assert.deepEqual(findController.getState().searchScope, [new Selection(1, 1, 2, 1), new Selection(2, 1, 2, 5)]); }); }); @@ -584,7 +590,7 @@ suite('FindController query options persistence', () => { loop: true }); - assert.deepEqual(findController.getState().searchScope, new Selection(1, 2, 1, 3)); + assert.deepEqual(findController.getState().searchScope, [new Selection(1, 2, 1, 3)]); }); }); @@ -609,7 +615,7 @@ suite('FindController query options persistence', () => { loop: true }); - assert.deepEqual(findController.getState().searchScope, new Selection(1, 6, 2, 1)); + assert.deepEqual(findController.getState().searchScope, [new Selection(1, 6, 2, 1)]); }); }); }); diff --git a/src/vs/editor/contrib/find/test/findModel.test.ts b/src/vs/editor/contrib/find/test/findModel.test.ts index 1a515fdddcc..218c52192a8 100644 --- a/src/vs/editor/contrib/find/test/findModel.test.ts +++ b/src/vs/editor/contrib/find/test/findModel.test.ts @@ -210,7 +210,7 @@ suite('FindModel', () => { ); // simulate adding a search scope - findState.change({ searchScope: new Range(8, 1, 10, 1) }, true); + findState.change({ searchScope: [new Range(8, 1, 10, 1)] }, true); assertFindState( editor, [8, 14, 8, 19], @@ -443,7 +443,7 @@ suite('FindModel', () => { findTest('find model next stays in scope', (editor) => { let findState = new FindReplaceState(); - findState.change({ searchString: 'hello', wholeWord: true, searchScope: new Range(7, 1, 9, 1) }, false); + findState.change({ searchString: 'hello', wholeWord: true, searchScope: [new Range(7, 1, 9, 1)] }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); assertFindState( @@ -493,6 +493,79 @@ suite('FindModel', () => { findState.dispose(); }); + findTest('multi-selection find model next stays in scope', (editor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello', matchCase: true, wholeWord: false, searchScope: [new Range(6, 1, 7, 38), new Range(9, 3, 9, 38)] }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 14, 6, 19], + // `matchCase: false` would + // find this match as well: + // [6, 27, 6, 32], + [7, 14, 7, 19], + // `wholeWord: true` would + // exclude this match: + [9, 14, 9, 19], + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [6, 14, 6, 19], + [6, 14, 6, 19], + [ + [6, 14, 6, 19], + [7, 14, 7, 19], + [9, 14, 9, 19], + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [7, 14, 7, 19], + [7, 14, 7, 19], + [ + [6, 14, 6, 19], + [7, 14, 7, 19], + [9, 14, 9, 19], + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [9, 14, 9, 19], + [9, 14, 9, 19], + [ + [6, 14, 6, 19], + [7, 14, 7, 19], + [9, 14, 9, 19], + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [6, 14, 6, 19], + [6, 14, 6, 19], + [ + [6, 14, 6, 19], + [7, 14, 7, 19], + [9, 14, 9, 19], + ] + ); + + findModel.dispose(); + findState.dispose(); + }); + findTest('find model prev', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'hello', wholeWord: true }, false); @@ -581,7 +654,7 @@ suite('FindModel', () => { findTest('find model prev stays in scope', (editor) => { let findState = new FindReplaceState(); - findState.change({ searchString: 'hello', wholeWord: true, searchScope: new Range(7, 1, 9, 1) }, false); + findState.change({ searchString: 'hello', wholeWord: true, searchScope: [new Range(7, 1, 9, 1)] }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); assertFindState( @@ -2073,7 +2146,7 @@ suite('FindModel', () => { findTest('issue #27083. search scope works even if it is a single line', (editor) => { let findState = new FindReplaceState(); - findState.change({ searchString: 'hello', wholeWord: true, searchScope: new Range(7, 1, 8, 1) }, false); + findState.change({ searchString: 'hello', wholeWord: true, searchScope: [new Range(7, 1, 8, 1)] }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); assertFindState( diff --git a/src/vs/editor/contrib/multicursor/multicursor.ts b/src/vs/editor/contrib/multicursor/multicursor.ts index cdb7d0c6190..4bf81a1ec8a 100644 --- a/src/vs/editor/contrib/multicursor/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/multicursor.ts @@ -601,13 +601,15 @@ export class MultiCursorSelectionController extends Disposable implements IEdito } if (findState.searchScope) { - const state = findState.searchScope; + const states = findState.searchScope; let inSelection: FindMatch[] | null = []; - for (let i = 0; i < matches.length; i++) { - if (matches[i].range.endLineNumber <= state.endLineNumber && matches[i].range.startLineNumber >= state.startLineNumber) { - inSelection.push(matches[i]); - } - } + matches.forEach((match) => { + states.forEach((state) => { + if (match.range.endLineNumber <= state.endLineNumber && match.range.startLineNumber >= state.startLineNumber) { + inSelection!.push(match); + } + }); + }); matches = inSelection; } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index e4e0752901d..987b9cd0533 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -1758,7 +1758,7 @@ declare namespace monaco.editor { /** * Search the model. * @param searchString The string used to search. If it is a regular expression, set `isRegex` to true. - * @param searchScope Limit the searching to only search inside this range. + * @param searchScope Limit the searching to only search inside these ranges. * @param isRegex Used to indicate that `searchString` is a regular expression. * @param matchCase Force the matching to match lower/upper case exactly. * @param wordSeparators Force the matching to match entire words only. Pass null otherwise. @@ -1766,7 +1766,7 @@ declare namespace monaco.editor { * @param limitResultCount Limit the number of results * @return The ranges where the matches are. It is empty if no matches have been found. */ - findMatches(searchString: string, searchScope: IRange, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean, limitResultCount?: number): FindMatch[]; + findMatches(searchString: string, searchScope: IRange | IRange[], isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean, limitResultCount?: number): FindMatch[]; /** * Search the model for the next match. Loops to the beginning of the model if needed. * @param searchString The string used to search. If it is a regular expression, set `isRegex` to true. From b3fba742c5846997eb7ef1f36a1860588eff681a Mon Sep 17 00:00:00 2001 From: daybrush Date: Sat, 4 Jul 2020 05:04:06 +0900 Subject: [PATCH 004/736] Fix cut & copy for iPad --- src/vs/editor/browser/controller/textAreaHandler.ts | 7 ------- src/vs/editor/test/browser/controller/imeTester.ts | 7 ------- 2 files changed, 14 deletions(-) diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index 9eee0dfe476..220c6c440ea 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -178,14 +178,7 @@ export class TextAreaHandler extends ViewPart { mode }; }, - getScreenReaderContent: (currentState: TextAreaState): TextAreaState => { - - if (browser.isIPad) { - // Do not place anything in the textarea for the iPad - return TextAreaState.EMPTY; - } - if (this._accessibilitySupport === AccessibilitySupport.Disabled) { // We know for a fact that a screen reader is not attached // On OSX, we write the character before the cursor to allow for "long-press" composition diff --git a/src/vs/editor/test/browser/controller/imeTester.ts b/src/vs/editor/test/browser/controller/imeTester.ts index 1668233bf9d..508aa9fc833 100644 --- a/src/vs/editor/test/browser/controller/imeTester.ts +++ b/src/vs/editor/test/browser/controller/imeTester.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as browser from 'vs/base/browser/browser'; import { createFastDomNode } from 'vs/base/browser/fastDomNode'; import { ITextAreaInputHost, TextAreaInput } from 'vs/editor/browser/controller/textAreaInput'; import { ISimpleModel, PagedScreenReaderStrategy, TextAreaState } from 'vs/editor/browser/controller/textAreaState'; @@ -96,12 +95,6 @@ function doCreateTest(description: string, inputStr: string, expectedStr: string }; }, getScreenReaderContent: (currentState: TextAreaState): TextAreaState => { - - if (browser.isIPad) { - // Do not place anything in the textarea for the iPad - return TextAreaState.EMPTY; - } - const selection = new Range(1, 1 + cursorOffset, 1, 1 + cursorOffset + cursorLength); return PagedScreenReaderStrategy.fromEditorSelection(currentState, model, selection, 10, true); From 00dea1c4ad670898678506a151e637d2775a103d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 17 Jul 2020 11:02:08 +0200 Subject: [PATCH 005/736] create "real" TextDocument when opening a notebook or when adding cells, don't create them again when the renderer makes models --- .../src/notebook.test.ts | 26 ++ src/vs/base/common/network.ts | 2 + .../browser/mainThreadDocumentsAndEditors.ts | 7 +- .../workbench/api/common/extHostDocuments.ts | 4 +- .../workbench/api/common/extHostNotebook.ts | 74 +++--- .../contrib/notebook/common/notebookCommon.ts | 2 +- .../test/browser/api/extHostNotebook.test.ts | 227 +++++++++--------- .../api/extHostNotebookConcatDocument.test.ts | 11 - 8 files changed, 188 insertions(+), 165 deletions(-) diff --git a/extensions/vscode-notebook-tests/src/notebook.test.ts b/extensions/vscode-notebook-tests/src/notebook.test.ts index 788585e3d4d..852e9c97d17 100644 --- a/extensions/vscode-notebook-tests/src/notebook.test.ts +++ b/extensions/vscode-notebook-tests/src/notebook.test.ts @@ -93,6 +93,32 @@ suite('Notebook API tests', () => { await firstDocumentClose; }); + test('notebook open/close, all cell-documents are ready', async function () { + const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb')); + + const p = new Promise((resolve, reject) => { + once(vscode.notebook.onDidOpenNotebookDocument)(notebook => { + try { + for (let cell of notebook.cells) { + const doc = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === cell.uri.toString()); + assert.ok(doc); + assert.strictEqual(doc === cell.document, true); + assert.strictEqual(doc?.languageId, cell.language); + assert.strictEqual(doc?.isDirty, false); + assert.strictEqual(doc?.isClosed, false); + } + resolve(); + } catch (err) { + reject(err); + } + }); + }); + + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + await p; + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); + test('shared document in notebook editors', async function () { const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb')); let counter = 0; diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index 1286c5117a4..4b6aebc1646 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -58,6 +58,8 @@ export namespace Schemas { export const vscodeNotebook = 'vscode-notebook'; + export const vscodeNotebookCell = 'vscode-notebook-cell'; + export const vscodeSettings = 'vscode-settings'; export const webviewPanel = 'webview-panel'; diff --git a/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts b/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts index 5f69d9ee9bc..537fc580a31 100644 --- a/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts @@ -30,6 +30,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { Schemas } from 'vs/base/common/network'; namespace delta { @@ -399,8 +400,12 @@ export class MainThreadDocumentsAndEditors { extHostDelta.removedEditors = removedEditors; } if (delta.addedDocuments.length > 0) { + // notebook cell documents get sync'd by the notebook open logic + // and therefore we don't send them another time now. empty = false; - extHostDelta.addedDocuments = delta.addedDocuments.map(m => this._toModelAddData(m)); + extHostDelta.addedDocuments = delta.addedDocuments + .filter(m => m.uri.scheme !== Schemas.vscodeNotebookCell) + .map(m => this._toModelAddData(m)); } if (delta.addedEditors.length > 0) { empty = false; diff --git a/src/vs/workbench/api/common/extHostDocuments.ts b/src/vs/workbench/api/common/extHostDocuments.ts index af4a3f9de52..ab8d770765b 100644 --- a/src/vs/workbench/api/common/extHostDocuments.ts +++ b/src/vs/workbench/api/common/extHostDocuments.ts @@ -69,8 +69,8 @@ export class ExtHostDocuments implements ExtHostDocumentsShape { public getDocument(resource: vscode.Uri): vscode.TextDocument { const data = this.getDocumentData(resource); - if (!data || !data.document) { - throw new Error('Unable to retrieve document from URI'); + if (!data?.document) { + throw new Error(`Unable to retrieve document from URI '${resource}'`); } return data.document; } diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index c55f9218edc..959d8c0d57a 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -11,14 +11,12 @@ import { ISplice } from 'vs/base/common/sequence'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as UUID from 'vs/base/common/uuid'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { CellKind, ExtHostNotebookShape, IMainContext, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice, MainThreadDocumentsShape, INotebookEditorPropertiesChangeData, INotebookDocumentsAndEditorsDelta } from 'vs/workbench/api/common/extHost.protocol'; +import { CellKind, ExtHostNotebookShape, IMainContext, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice, INotebookEditorPropertiesChangeData, INotebookDocumentsAndEditorsDelta, IModelAddedData } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { CellEditType, diff, ICellEditOperation, ICellInsertEdit, INotebookDisplayOrder, INotebookEditData, NotebookCellsChangedEvent, NotebookCellsSplice2, ICellDeleteEdit, notebookDocumentMetadataDefaults, NotebookCellsChangeType, NotebookDataDto, IOutputRenderRequest, IOutputRenderResponse, IOutputRenderResponseOutputInfo, IOutputRenderResponseCellInfo, IRawOutput, CellOutputKind, IProcessedOutput, INotebookKernelInfoDto2, IMainCellDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData'; -import { NotImplementedProxy } from 'vs/base/common/types'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; @@ -57,28 +55,19 @@ interface INotebookEventEmitter { const addIdToOutput = (output: IRawOutput, id = UUID.generateUuid()): IProcessedOutput => output.outputKind === CellOutputKind.Rich ? ({ ...output, outputId: id }) : output; -class DettachedCellDocumentData extends ExtHostDocumentData { - - private static readonly _fakeProxy = new class extends NotImplementedProxy('document') { - $trySaveDocument() { - return Promise.reject('Cell-document cannot be saved'); - } - }; - - constructor(cell: IMainCellDto) { - super(DettachedCellDocumentData._fakeProxy, - URI.revive(cell.uri), - cell.source, - cell.eol, - cell.language, - 0, - false - ); - } -} - export class ExtHostCell extends Disposable implements vscode.NotebookCell { + public static asModelAddData(cell: IMainCellDto): IModelAddedData { + return { + EOL: cell.eol, + lines: cell.source, + modeId: cell.language, + uri: cell.uri, + isDirty: false, + versionId: 1 + }; + } + private _onDidChangeOutputs = new Emitter[]>(); readonly onDidChangeOutputs: Event[]> = this._onDidChangeOutputs.event; @@ -92,13 +81,6 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell { readonly uri: URI; readonly cellKind: CellKind; - // todo@jrieken this is a little fish because we have - // vscode.TextDocument for which we never fired an onDidOpen - // event and which doesn't appear in the list of documents. - // this will change once the "real" document comes along. We - // should come up with a better approach here... - readonly defaultDocument: DettachedCellDocumentData; - constructor( private _proxy: MainThreadNotebookShape, readonly notebook: ExtHostNotebookDocument, @@ -110,7 +92,6 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell { this.handle = cell.handle; this.uri = URI.revive(cell.uri); this.cellKind = cell.cellKind; - this.defaultDocument = new DettachedCellDocumentData(cell); this._outputs = cell.outputs; for (const output of this._outputs) { @@ -126,7 +107,7 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell { } get document(): vscode.TextDocument { - return this._extHostDocument.getDocument(this.uri)?.document ?? this.defaultDocument.document; + return this._extHostDocument.getDocument(this.uri)!.document; } get language(): string { @@ -368,7 +349,8 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo return; } - let contentChangeEvents: vscode.NotebookCellsChangeData[] = []; + const contentChangeEvents: vscode.NotebookCellsChangeData[] = []; + const addedCellDocuments: IModelAddedData[] = []; splices.reverse().forEach(splice => { let cellDtos = splice[2]; @@ -376,6 +358,10 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo const extCell = new ExtHostCell(this._proxy, this, this._documentsAndEditors, cell); + if (!initialization) { + addedCellDocuments.push(ExtHostCell.asModelAddData(cell)); + } + if (!this._cellDisposableMapping.has(extCell.handle)) { this._cellDisposableMapping.set(extCell.handle, new DisposableStore()); } @@ -392,21 +378,22 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo for (let j = splice[0]; j < splice[0] + splice[1]; j++) { this._cellDisposableMapping.get(this.cells[j].handle)?.dispose(); this._cellDisposableMapping.delete(this.cells[j].handle); - } const deletedItems = this.cells.splice(splice[0], splice[1], ...newCells); - const event: vscode.NotebookCellsChangeData = { + contentChangeEvents.push({ start: splice[0], deletedCount: splice[1], deletedItems, items: newCells - }; - - contentChangeEvents.push(event); + }); }); + if (addedCellDocuments) { + this._documentsAndEditors.$acceptDocumentsAndEditorsDelta({ addedDocuments: addedCellDocuments }); + } + if (!initialization) { this._emitter.emitModelChange({ document: this, @@ -456,7 +443,6 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo private $changeCellLanguage(index: number, language: string): void { const cell = this.cells[index]; - cell.defaultDocument._acceptLanguageId(language); const event: vscode.NotebookCellLanguageChangeEvent = { document: this, cell, language }; this._emitter.emitCellLanguageChange(event); } @@ -1427,6 +1413,9 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } if (delta.addedDocuments) { + + const addedCellDocuments: IModelAddedData[] = []; + delta.addedDocuments.forEach(modelData => { const revivedUri = URI.revive(modelData.uri); const revivedUriStr = revivedUri.toString(); @@ -1470,6 +1459,9 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN ]] }); + // add cell document as vscode.TextDocument + addedCellDocuments.push(...modelData.cells.map(ExtHostCell.asModelAddData)); + this._documents.get(revivedUriStr)?.dispose(); this._documents.set(revivedUriStr, document); @@ -1480,6 +1472,10 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } } + this._documentsAndEditors.$acceptDocumentsAndEditorsDelta({ + addedDocuments: addedCellDocuments + }); + const document = this._documents.get(revivedUriStr)!; this._onDidOpenNotebookDocument.fire(document); }); diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index e7063754ffe..5582306d02f 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -397,7 +397,7 @@ export interface NotebookDataDto { export namespace CellUri { - export const scheme = 'vscode-notebook-cell'; + export const scheme = Schemas.vscodeNotebookCell; const _regex = /^\d{7,}/; export function generate(notebook: URI, handle: number): URI { diff --git a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts index 4f4d4967242..c793c27a5fa 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts @@ -4,148 +4,153 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import * as vscode from 'vscode'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { TestRPCProtocol } from 'vs/workbench/test/browser/api/testRPCProtocol'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { NullLogService } from 'vs/platform/log/common/log'; import { mock } from 'vs/base/test/common/mock'; -import { MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; -import { ExtHostNotebookDocument, ExtHostCell } from 'vs/workbench/api/common/extHostNotebook'; -import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { MainContext, MainThreadCommandsShape, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostNotebookDocument, ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; +import { CellKind, CellUri, NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { URI } from 'vs/base/common/uri'; import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; +import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; +import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { isEqual } from 'vs/base/common/resources'; + +suite('NotebookCell#Document', function () { -suite('NotebookCell', function () { let rpcProtocol: TestRPCProtocol; + let notebook: ExtHostNotebookDocument; let extHostDocumentsAndEditors: ExtHostDocumentsAndEditors; - + let extHostDocuments: ExtHostDocuments; + let extHostNotebooks: ExtHostNotebookController; + const notebookUri = URI.parse('test:///notebook.file'); const disposables = new DisposableStore(); - const fakeNotebookProxy = new class extends mock() { }; - const fakeNotebook = new class extends mock() { }; setup(async function () { disposables.clear(); + rpcProtocol = new TestRPCProtocol(); + rpcProtocol.set(MainContext.MainThreadCommands, new class extends mock() { + $registerCommand() { } + }); + rpcProtocol.set(MainContext.MainThreadNotebook, new class extends mock() { + async $registerNotebookProvider() { } + async $unregisterNotebookProvider() { } + }); extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(rpcProtocol, new NullLogService()); - }); - - test('Document is real', function () { - - const dto = { - cellKind: CellKind.Code, - eol: '\n', - source: ['aaaa', 'bbbb', 'cccc'], - handle: 0, - language: 'fooLang', - outputs: [], - uri: URI.parse('test:/path') - }; - const cell = new ExtHostCell(fakeNotebookProxy, fakeNotebook, extHostDocumentsAndEditors, dto); - - assert.ok(cell.document); - assert.strictEqual(cell.document.version, 0); - assert.strictEqual(cell.document.languageId, dto.language); - assert.strictEqual(cell.document.uri.toString(), dto.uri.toString()); - assert.strictEqual(cell.uri.toString(), dto.uri.toString()); - }); - - - test('Document is uses actual document when possible', function () { - - const dto = { - cellKind: CellKind.Code, - eol: '\n', - source: ['aaaa', 'bbbb', 'cccc'], - handle: 0, - language: 'fooLang', - outputs: [], - uri: URI.parse('test:/path') - }; - const cell = new ExtHostCell(fakeNotebookProxy, fakeNotebook, extHostDocumentsAndEditors, dto); - - // this is the "default document" which is used when the real - // document isn't open - const documentNow = cell.document; - - extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ + extHostDocuments = new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors); + extHostNotebooks = new ExtHostNotebookController(rpcProtocol, new ExtHostCommands(rpcProtocol, new NullLogService()), extHostDocumentsAndEditors, { isExtensionDevelopmentDebug: false, webviewCspSource: '', webviewResourceRoot: '' }); + let reg = extHostNotebooks.registerNotebookContentProvider(nullExtensionDescription, 'test', new class extends mock() { + // async openNotebook() { } + }); + await extHostNotebooks.$acceptDocumentAndEditorsDelta({ addedDocuments: [{ - isDirty: false, - versionId: 12, - modeId: dto.language, - uri: dto.uri, - lines: dto.source, - EOL: dto.eol + handle: 0, + uri: notebookUri, + viewType: 'test', + versionId: 0, + cells: [{ + handle: 0, + uri: CellUri.generate(notebookUri, 0), + source: ['### Heading'], + eol: '\n', + language: 'markdown', + cellKind: CellKind.Markdown, + outputs: [], + }, { + handle: 1, + uri: CellUri.generate(notebookUri, 1), + source: ['console.log("aaa")', 'console.log("bbb")'], + eol: '\n', + language: 'javascript', + cellKind: CellKind.Code, + outputs: [], + }], + }], + addedEditors: [{ + documentUri: notebookUri, + id: '_notebook_editor_0', + selections: [0] }] }); + await extHostNotebooks.$acceptDocumentAndEditorsDelta({ newActiveEditor: '_notebook_editor_0' }); - // the real document - assert.ok(documentNow !== cell.document); - assert.strictEqual(cell.document.languageId, dto.language); - assert.strictEqual(cell.document.uri.toString(), dto.uri.toString()); - assert.strictEqual(cell.uri.toString(), dto.uri.toString()); + notebook = extHostNotebooks.notebookDocuments[0]!; - // back to "default document" - extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ removedDocuments: [dto.uri] }); - assert.ok(documentNow === cell.document); - }); - - test('Document can change language (1/2)', function () { - - const dto = { - cellKind: CellKind.Code, - eol: '\n', - source: ['aaaa', 'bbbb', 'cccc'], - handle: 0, - language: 'fooLang', - outputs: [], - uri: URI.parse('test:/path') - }; - const cell = new ExtHostCell(fakeNotebookProxy, fakeNotebook, extHostDocumentsAndEditors, dto); - - assert.strictEqual(cell.document.languageId, dto.language); - cell.defaultDocument._acceptLanguageId('barLang'); - assert.strictEqual(cell.document.languageId, 'barLang'); + disposables.add(reg); + disposables.add(notebook); + disposables.add(extHostDocuments); }); - test('Document can change language (1/2)', function () { + test('cell document is vscode.TextDocument', async function () { + assert.strictEqual(notebook.cells.length, 2); - const dto = { - cellKind: CellKind.Code, - eol: '\n', - source: ['aaaa', 'bbbb', 'cccc'], - handle: 0, - language: 'fooLang', - outputs: [], - uri: URI.parse('test:/path') - }; + const [c1, c2] = notebook.cells; + const d1 = extHostDocuments.getDocument(c1.uri); - extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ - addedDocuments: [{ - isDirty: false, - versionId: 12, - modeId: dto.language, - uri: dto.uri, - lines: dto.source, - EOL: dto.eol - }] + assert.ok(d1); + assert.equal(d1.languageId, c1.language); + + const d2 = extHostDocuments.getDocument(c2.uri); + assert.ok(d2); + assert.equal(d2.languageId, c2.language); + }); + + test('cell document is vscode.TextDocument after changing it', async function () { + + const p = new Promise((resolve, reject) => { + extHostNotebooks.onDidChangeNotebookCells(e => { + try { + assert.strictEqual(e.changes.length, 1); + assert.strictEqual(e.changes[0].items.length, 2); + + const [first, second] = e.changes[0].items; + + const doc1 = extHostDocuments.getAllDocumentData().find(data => isEqual(data.document.uri, first.uri)); + assert.ok(doc1); + assert.strictEqual(doc1?.document === first.document, true); + + const doc2 = extHostDocuments.getAllDocumentData().find(data => isEqual(data.document.uri, second.uri)); + assert.ok(doc2); + assert.strictEqual(doc2?.document === second.document, true); + + resolve(); + + } catch (err) { + reject(err); + } + }); }); - const extHostDocuments = new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors); + extHostNotebooks.$acceptModelChanged(notebookUri, { + kind: NotebookCellsChangeType.ModelChange, + versionId: notebook.versionId + 1, + changes: [[0, 0, [{ + handle: 2, + uri: CellUri.generate(notebookUri, 2), + source: ['Hello', 'World', 'Hello World!'], + eol: '\n', + language: 'test', + cellKind: CellKind.Code, + outputs: [], + }, { + handle: 3, + uri: CellUri.generate(notebookUri, 3), + source: ['Hallo', 'Welt', 'Hallo Welt!'], + eol: '\n', + language: 'test', + cellKind: CellKind.Code, + outputs: [], + }]]] + }); - const cell = new ExtHostCell(fakeNotebookProxy, fakeNotebook, extHostDocumentsAndEditors, dto); + await p; - // a real document already exists and therefore - // the "default document" doesn't count - - assert.strictEqual(cell.document.languageId, dto.language); - cell.defaultDocument._acceptLanguageId('barLang'); - assert.strictEqual(cell.document.languageId, dto.language); - - extHostDocuments.$acceptModelModeChanged(dto.uri, dto.language, 'barLang'); - assert.strictEqual(cell.document.languageId, 'barLang'); }); - }); diff --git a/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts b/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts index 3fcaa6da684..c55d9dede7c 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts @@ -292,17 +292,6 @@ suite('NotebookConcatDocument', function () { let cell1End = doc.offsetAt(new Position(2, 12)); assert.equal(doc.positionAt(cell1End).isEqual(new Position(2, 12)), true); - extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ - addedDocuments: [{ - uri: notebook.cells[0].uri, - versionId: 1, - lines: ['Hello', 'World', 'Hello World!'], - EOL: '\n', - modeId: '', - isDirty: false - }] - }); - extHostDocuments.$acceptModelChanged(notebook.cells[0].uri, { versionId: 0, eol: '\n', From 04eef0779dedb100f335bdad3c54459b4d70fbcf Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 17 Jul 2020 11:16:38 +0200 Subject: [PATCH 006/736] add test that asserts cell document open are fired when the notebook is ready, not before --- .../src/notebook.test.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/extensions/vscode-notebook-tests/src/notebook.test.ts b/extensions/vscode-notebook-tests/src/notebook.test.ts index 852e9c97d17..5bd7a32ab2a 100644 --- a/extensions/vscode-notebook-tests/src/notebook.test.ts +++ b/extensions/vscode-notebook-tests/src/notebook.test.ts @@ -119,6 +119,27 @@ suite('Notebook API tests', () => { await vscode.commands.executeCommand('workbench.action.closeAllEditors'); }); + test('notebook open/close, notebook ready when cell-document open event is fired', async function () { + const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb')); + let didHappen = false; + const p = getEventOncePromise(vscode.workspace.onDidOpenTextDocument).then(doc => { + if (doc.uri.scheme !== 'vscode-notebook-cell') { + return; + } + const notebook = vscode.notebook.notebookDocuments.find(notebook => { + const cell = notebook.cells.find(cell => cell.document === doc); + return Boolean(cell); + }); + assert.ok(notebook, `notebook for cell ${doc.uri} NOT found`); + didHappen = true; + }); + + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + await p; + assert.strictEqual(didHappen, true); + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); + test('shared document in notebook editors', async function () { const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb')); let counter = 0; From b3f090d3296471d8d2e16601f7ceeb43216da3e3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 17 Jul 2020 11:18:35 +0200 Subject: [PATCH 007/736] use getEventOncePromise-util --- .../src/notebook.test.ts | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/extensions/vscode-notebook-tests/src/notebook.test.ts b/extensions/vscode-notebook-tests/src/notebook.test.ts index 5bd7a32ab2a..55862568d13 100644 --- a/extensions/vscode-notebook-tests/src/notebook.test.ts +++ b/extensions/vscode-notebook-tests/src/notebook.test.ts @@ -96,22 +96,15 @@ suite('Notebook API tests', () => { test('notebook open/close, all cell-documents are ready', async function () { const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb')); - const p = new Promise((resolve, reject) => { - once(vscode.notebook.onDidOpenNotebookDocument)(notebook => { - try { - for (let cell of notebook.cells) { - const doc = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === cell.uri.toString()); - assert.ok(doc); - assert.strictEqual(doc === cell.document, true); - assert.strictEqual(doc?.languageId, cell.language); - assert.strictEqual(doc?.isDirty, false); - assert.strictEqual(doc?.isClosed, false); - } - resolve(); - } catch (err) { - reject(err); - } - }); + const p = getEventOncePromise(vscode.notebook.onDidOpenNotebookDocument).then(notebook => { + for (let cell of notebook.cells) { + const doc = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === cell.uri.toString()); + assert.ok(doc); + assert.strictEqual(doc === cell.document, true); + assert.strictEqual(doc?.languageId, cell.language); + assert.strictEqual(doc?.isDirty, false); + assert.strictEqual(doc?.isClosed, false); + } }); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); From b2666580914d34630f0b0e668d7dac008d1f823b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 17 Jul 2020 14:52:25 +0200 Subject: [PATCH 008/736] remove document that only the notebook did open --- src/vs/workbench/api/common/extHostNotebook.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 959d8c0d57a..f973ad18c72 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import { readonly } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { ISplice } from 'vs/base/common/sequence'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as UUID from 'vs/base/common/uuid'; @@ -64,7 +64,7 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell { modeId: cell.language, uri: cell.uri, isDirty: false, - versionId: 1 + versionId: -1 }; } @@ -320,7 +320,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo dispose() { this._disposed = true; super.dispose(); - this._cellDisposableMapping.forEach(cell => cell.dispose()); + dispose(this._cellDisposableMapping.values()); } get fileName() { return this.uri.fsPath; } @@ -1397,8 +1397,19 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN let document = this._documents.get(revivedUriStr); if (document) { + + // remove all documents that have not been opened by the renderer + // and have not yet been closed + const removedCellDocuments: URI[] = []; + for (let cell of document.cells) { + if (this._documentsAndEditors.getDocument(cell.uri)?.version === -1) { + removedCellDocuments.push(cell.uri); + } + } + document.dispose(); this._documents.delete(revivedUriStr); + this._documentsAndEditors.$acceptDocumentsAndEditorsDelta({ removedDocuments: removedCellDocuments }); this._onDidCloseNotebookDocument.fire(document); } From 39774a2d8d416c3996ea13107c5744be620bc119 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 17 Jul 2020 18:14:00 +0200 Subject: [PATCH 009/736] some more unit testing --- .../test/browser/api/extHostNotebook.test.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts index c793c27a5fa..ccf9dbe7347 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts @@ -96,10 +96,31 @@ suite('NotebookCell#Document', function () { assert.ok(d1); assert.equal(d1.languageId, c1.language); + assert.equal(d1.version, -1); // we use -1 as signal that the document was created on the ext-host side const d2 = extHostDocuments.getDocument(c2.uri); assert.ok(d2); assert.equal(d2.languageId, c2.language); + assert.equal(d2.version, -1); + }); + + test('cell document goes when notebook closes', async function () { + const cellUris: string[] = []; + for (let cell of notebook.cells) { + assert.ok(extHostDocuments.getDocument(cell.uri)); + cellUris.push(cell.uri.toString()); + } + + const removedCellUris: string[] = []; + const reg = extHostDocuments.onDidRemoveDocument(doc => { + removedCellUris.push(doc.uri.toString()); + }); + + await extHostNotebooks.$acceptDocumentAndEditorsDelta({ removedDocuments: [notebook.uri] }); + reg.dispose(); + + assert.strictEqual(removedCellUris.length, 2); + assert.deepStrictEqual(removedCellUris.sort(), cellUris.sort()); }); test('cell document is vscode.TextDocument after changing it', async function () { From 660ffebf0a886f91e930121275b2b9cced300363 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 17 Jul 2020 18:23:16 +0200 Subject: [PATCH 010/736] always send model add data and check on the extension if this is a notebook-cell special case this sends the twice but gurantees that the renderer owns the model --- .../api/browser/mainThreadDocumentsAndEditors.ts | 7 +------ .../workbench/api/common/extHostDocumentsAndEditors.ts | 10 +++++++++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts b/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts index 537fc580a31..5f69d9ee9bc 100644 --- a/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts @@ -30,7 +30,6 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { Schemas } from 'vs/base/common/network'; namespace delta { @@ -400,12 +399,8 @@ export class MainThreadDocumentsAndEditors { extHostDelta.removedEditors = removedEditors; } if (delta.addedDocuments.length > 0) { - // notebook cell documents get sync'd by the notebook open logic - // and therefore we don't send them another time now. empty = false; - extHostDelta.addedDocuments = delta.addedDocuments - .filter(m => m.uri.scheme !== Schemas.vscodeNotebookCell) - .map(m => this._toModelAddData(m)); + extHostDelta.addedDocuments = delta.addedDocuments.map(m => this._toModelAddData(m)); } if (delta.addedEditors.length > 0) { empty = false; diff --git a/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts b/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts index bf3919a0841..63fc98becaa 100644 --- a/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts +++ b/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts @@ -15,6 +15,7 @@ import { ExtHostTextEditor } from 'vs/workbench/api/common/extHostTextEditor'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { ILogService } from 'vs/platform/log/common/log'; import { ResourceMap } from 'vs/base/common/map'; +import { Schemas } from 'vs/base/common/network'; export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsShape { @@ -60,7 +61,14 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha if (delta.addedDocuments) { for (const data of delta.addedDocuments) { const resource = URI.revive(data.uri); - assert.ok(!this._documents.has(resource), `document '${resource} already exists!'`); + const existingDocumentData = this._documents.get(resource); + if (existingDocumentData) { + if (resource.scheme !== Schemas.vscodeNotebookCell) { + throw new Error(`document '${resource} already exists!'`); + } + existingDocumentData.onEvents({ changes: [], versionId: data.versionId, eol: data.EOL }); + continue; + } const documentData = new ExtHostDocumentData( this._extHostRpc.getProxy(MainContext.MainThreadDocuments), From 10a5d09ed518776df2f5f8df5ad851f76def0aee Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Thu, 23 Jul 2020 15:40:13 -0700 Subject: [PATCH 011/736] Add DebugAdapterNamedPipeServer --- src/vs/vscode.d.ts | 15 +++ .../workbench/api/common/extHost.api.impl.ts | 1 + .../api/common/extHostDebugService.ts | 9 +- src/vs/workbench/api/common/extHostTypes.ts | 6 ++ .../workbench/api/node/extHostDebugService.ts | 4 +- .../workbench/contrib/debug/common/debug.ts | 7 +- .../contrib/debug/node/debugAdapter.ts | 47 +++++++++- .../test/node/streamDebugAdapter.test.ts | 94 +++++++++++++++++++ 8 files changed, 178 insertions(+), 5 deletions(-) create mode 100644 src/vs/workbench/contrib/debug/test/node/streamDebugAdapter.test.ts diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index efd2b3b06e4..8df7d0ef548 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -10790,6 +10790,21 @@ declare module 'vscode' { constructor(port: number, host?: string); } + /** + * Represents a debug adapter running as a Named Pipe (on Windows)/UNIX Domain Socket (on non-Windows) based server. + */ + export class DebugAdapterNamedPipeServer { + /** + * The path to the NamedPipe/UNIX Domain Socket. + */ + readonly path: string; + + /** + * Create a description for a debug adapter running as a socket based server. + */ + constructor(path: string); + } + /** * A debug adapter that implements the Debug Adapter Protocol can be registered with VS Code if it implements the DebugAdapter interface. */ diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 036366842bf..db0d8cdd2c0 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1016,6 +1016,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I ConfigurationTarget: extHostTypes.ConfigurationTarget, DebugAdapterExecutable: extHostTypes.DebugAdapterExecutable, DebugAdapterServer: extHostTypes.DebugAdapterServer, + DebugAdapterNamedPipeServer: extHostTypes.DebugAdapterNamedPipeServer, DebugAdapterInlineImplementation: extHostTypes.DebugAdapterInlineImplementation, DecorationRangeBehavior: extHostTypes.DecorationRangeBehavior, Diagnostic: extHostTypes.Diagnostic, diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index f9dcbecccef..3d302655fb1 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -11,12 +11,12 @@ import { MainContext, MainThreadDebugServiceShape, ExtHostDebugServiceShape, DebugSessionUUID, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto } from 'vs/workbench/api/common/extHost.protocol'; -import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint, DebugAdapterServer, DebugAdapterExecutable, DataBreakpoint, DebugConsoleMode, DebugAdapterInlineImplementation } from 'vs/workbench/api/common/extHostTypes'; +import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint, DebugAdapterServer, DebugAdapterExecutable, DataBreakpoint, DebugConsoleMode, DebugAdapterInlineImplementation, DebugAdapterNamedPipeServer } from 'vs/workbench/api/common/extHostTypes'; import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter'; import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; import { ExtHostDocumentsAndEditors, IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; -import { IDebuggerContribution, IConfig, IDebugAdapter, IDebugAdapterServer, IDebugAdapterExecutable, IAdapterDescriptor, IDebugAdapterImpl } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebuggerContribution, IConfig, IDebugAdapter, IDebugAdapterServer, IDebugAdapterExecutable, IAdapterDescriptor, IDebugAdapterImpl, IDebugAdapterNamedPipeServer } from 'vs/workbench/contrib/debug/common/debug'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/common/variableResolver'; import { ExtHostConfigProvider, IExtHostConfiguration } from '../common/extHostConfiguration'; @@ -737,6 +737,11 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E port: x.port, host: x.host }; + } else if (x instanceof DebugAdapterNamedPipeServer) { + return { + type: 'pipeServer', + path: x.path + }; } else if (x instanceof DebugAdapterInlineImplementation) { return { type: 'implementation', diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 447fa8092c6..66deb3afe2b 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2288,6 +2288,12 @@ export class DebugAdapterServer implements vscode.DebugAdapterServer { } } +@es5ClassCompat +export class DebugAdapterNamedPipeServer implements vscode.DebugAdapterNamedPipeServer { + constructor(public readonly path: string) { + } +} + @es5ClassCompat export class DebugAdapterInlineImplementation implements vscode.DebugAdapterInlineImplementation { readonly implementation: vscode.DebugAdapter; diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index 7d3cc3b33c3..4206b06776a 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import type * as vscode from 'vscode'; import * as env from 'vs/base/common/platform'; import { DebugAdapterExecutable } from 'vs/workbench/api/common/extHostTypes'; -import { ExecutableDebugAdapter, SocketDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter'; +import { ExecutableDebugAdapter, SocketDebugAdapter, NamedPipeDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter'; import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter'; import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; @@ -49,6 +49,8 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { switch (adapter.type) { case 'server': return new SocketDebugAdapter(adapter); + case 'pipeServer': + return new NamedPipeDebugAdapter(adapter); case 'executable': return new ExecutableDebugAdapter(adapter, session.type); } diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 620831e332c..ce399761320 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -565,6 +565,11 @@ export interface IDebugAdapterServer { readonly host?: string; } +export interface IDebugAdapterNamedPipeServer { + readonly type: 'pipeServer'; + readonly path: string; +} + export interface IDebugAdapterInlineImpl extends IDisposable { readonly onDidSendMessage: Event; handleMessage(message: DebugProtocol.Message): void; @@ -575,7 +580,7 @@ export interface IDebugAdapterImpl { readonly implementation: IDebugAdapterInlineImpl; } -export type IAdapterDescriptor = IDebugAdapterExecutable | IDebugAdapterServer | IDebugAdapterImpl; +export type IAdapterDescriptor = IDebugAdapterExecutable | IDebugAdapterServer | IDebugAdapterNamedPipeServer | IDebugAdapterImpl; export interface IPlatformSpecificAdapterContribution { program?: string; diff --git a/src/vs/workbench/contrib/debug/node/debugAdapter.ts b/src/vs/workbench/contrib/debug/node/debugAdapter.ts index 70ddc23e1b5..659e03c1a06 100644 --- a/src/vs/workbench/contrib/debug/node/debugAdapter.ts +++ b/src/vs/workbench/contrib/debug/node/debugAdapter.ts @@ -14,7 +14,7 @@ import * as objects from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; import { ExtensionsChannelId } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IOutputService } from 'vs/workbench/contrib/output/common/output'; -import { IDebugAdapterExecutable, IDebuggerContribution, IPlatformSpecificAdapterContribution, IDebugAdapterServer } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugAdapterExecutable, IDebuggerContribution, IPlatformSpecificAdapterContribution, IDebugAdapterServer, IDebugAdapterNamedPipeServer } from 'vs/workbench/contrib/debug/common/debug'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { AbstractDebugAdapter } from '../common/abstractDebugAdapter'; @@ -136,6 +136,51 @@ export class SocketDebugAdapter extends StreamDebugAdapter { } } +/** + * An implementation that connects to a debug adapter via a NamedPipe (on Windows)/UNIX Domain Socket (on non-Windows). + */ +export class NamedPipeDebugAdapter extends StreamDebugAdapter { + + private socket?: net.Socket; + + constructor(private adapterServer: IDebugAdapterNamedPipeServer) { + super(); + } + + startSession(): Promise { + return new Promise((resolve, reject) => { + let connected = false; + this.socket = net.createConnection(this.adapterServer.path, () => { + this.connect(this.socket!, this.socket!); + resolve(); + connected = true; + }); + this.socket.on('close', () => { + if (connected) { + this._onError.fire(new Error('connection closed')); + } else { + reject(new Error('connection closed')); + } + }); + this.socket.on('error', error => { + if (connected) { + this._onError.fire(error); + } else { + reject(error); + } + }); + }); + } + + async stopSession(): Promise { + await this.cancelPendingRequests(); + if (this.socket) { + this.socket.end(); + this.socket = undefined; + } + } +} + /** * An implementation that launches the debug adapter as a separate process and communicates via stdin/stdout. */ diff --git a/src/vs/workbench/contrib/debug/test/node/streamDebugAdapter.test.ts b/src/vs/workbench/contrib/debug/test/node/streamDebugAdapter.test.ts new file mode 100644 index 00000000000..1564c3d22f8 --- /dev/null +++ b/src/vs/workbench/contrib/debug/test/node/streamDebugAdapter.test.ts @@ -0,0 +1,94 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import * as net from 'net'; +import * as platform from 'vs/base/common/platform'; +import { tmpdir } from 'os'; +import { join } from 'vs/base/common/path'; +import { SocketDebugAdapter, NamedPipeDebugAdapter, StreamDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter'; + +function rndPort(): number { + const min = 8000; + const max = 9000; + return Math.floor(Math.random() * (max - min) + min); +} + +function rndName(): string { + return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10); +} + +function sendInitializeRequest(debugAdapter: StreamDebugAdapter): Promise { + return new Promise((resolve, reject) => { + debugAdapter.sendRequest('initialize', { adapterID: 'test' }, (result) => { + resolve(result); + }); + }); +} + +function serverConnection(socket: net.Socket) { + socket.on('data', (data: Buffer) => { + const str = data.toString().split('\r\n')[2]; + const request = JSON.parse(str); + const response: any = { + seq: request.seq, + request_seq: request.seq, + type: 'response', + command: request.command + }; + if (request.arguments.adapterID === 'test') { + response.success = true; + } else { + response.success = false; + response.message = 'failed'; + } + + const responsePayload = JSON.stringify(response); + socket.write(`Content-Length: ${responsePayload.length}\r\n\r\n${responsePayload}`); + }); +} + +suite('Debug - StreamDebugAdapter', () => { + const port = rndPort(); + const pipeName = rndName(); + const pipePath = platform.isWindows ? join('\\\\.\\pipe\\', pipeName) : join(tmpdir(), pipeName); + + const testCases: { testName: string, debugAdapter: StreamDebugAdapter, connectionDetail: string | number }[] = [ + { + testName: 'NamedPipeDebugAdapter', + debugAdapter: new NamedPipeDebugAdapter({ + type: 'pipeServer', + path: pipePath + }), + connectionDetail: pipePath + }, + { + testName: 'SocketDebugAdapter', + debugAdapter: new SocketDebugAdapter({ + type: 'server', + port + }), + connectionDetail: port + } + ]; + + for (const testCase of testCases) { + test(`StreamDebugAdapter (${testCase.testName}) can initialize a connection`, async () => { + const server = net.createServer(serverConnection).listen(testCase.connectionDetail); + const debugAdapter = testCase.debugAdapter; + try { + await debugAdapter.startSession(); + const response: DebugProtocol.Response = await sendInitializeRequest(debugAdapter); + assert.strictEqual(response.command, 'initialize'); + assert.strictEqual(response.request_seq, 1); + assert.strictEqual(response.success, true, response.message); + } finally { + await debugAdapter.stopSession(); + server.close(); + debugAdapter.dispose(); + } + }); + } +}); From 0b103238b73423655376d5280005e42e13f1be57 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Thu, 23 Jul 2020 16:53:11 -0700 Subject: [PATCH 012/736] refactor to abstract class and use crypto.randomBytes --- .../contrib/debug/node/debugAdapter.ts | 39 ++++++++----------- .../test/node/streamDebugAdapter.test.ts | 7 +--- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/contrib/debug/node/debugAdapter.ts b/src/vs/workbench/contrib/debug/node/debugAdapter.ts index 659e03c1a06..debe9451071 100644 --- a/src/vs/workbench/contrib/debug/node/debugAdapter.ts +++ b/src/vs/workbench/contrib/debug/node/debugAdapter.ts @@ -91,12 +91,25 @@ export abstract class StreamDebugAdapter extends AbstractDebugAdapter { } } +export abstract class NetworkDebugAdapter extends StreamDebugAdapter { + + protected socket?: net.Socket; + + abstract startSession(): Promise; + + async stopSession(): Promise { + await this.cancelPendingRequests(); + if (this.socket) { + this.socket.end(); + this.socket = undefined; + } + } +} + /** * An implementation that connects to a debug adapter via a socket. */ -export class SocketDebugAdapter extends StreamDebugAdapter { - - private socket?: net.Socket; +export class SocketDebugAdapter extends NetworkDebugAdapter { constructor(private adapterServer: IDebugAdapterServer) { super(); @@ -126,22 +139,12 @@ export class SocketDebugAdapter extends StreamDebugAdapter { }); }); } - - async stopSession(): Promise { - await this.cancelPendingRequests(); - if (this.socket) { - this.socket.end(); - this.socket = undefined; - } - } } /** * An implementation that connects to a debug adapter via a NamedPipe (on Windows)/UNIX Domain Socket (on non-Windows). */ -export class NamedPipeDebugAdapter extends StreamDebugAdapter { - - private socket?: net.Socket; +export class NamedPipeDebugAdapter extends NetworkDebugAdapter { constructor(private adapterServer: IDebugAdapterNamedPipeServer) { super(); @@ -171,14 +174,6 @@ export class NamedPipeDebugAdapter extends StreamDebugAdapter { }); }); } - - async stopSession(): Promise { - await this.cancelPendingRequests(); - if (this.socket) { - this.socket.end(); - this.socket = undefined; - } - } } /** diff --git a/src/vs/workbench/contrib/debug/test/node/streamDebugAdapter.test.ts b/src/vs/workbench/contrib/debug/test/node/streamDebugAdapter.test.ts index 1564c3d22f8..52977226fa5 100644 --- a/src/vs/workbench/contrib/debug/test/node/streamDebugAdapter.test.ts +++ b/src/vs/workbench/contrib/debug/test/node/streamDebugAdapter.test.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import * as crypto from 'crypto'; import * as net from 'net'; import * as platform from 'vs/base/common/platform'; import { tmpdir } from 'os'; @@ -16,10 +17,6 @@ function rndPort(): number { return Math.floor(Math.random() * (max - min) + min); } -function rndName(): string { - return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10); -} - function sendInitializeRequest(debugAdapter: StreamDebugAdapter): Promise { return new Promise((resolve, reject) => { debugAdapter.sendRequest('initialize', { adapterID: 'test' }, (result) => { @@ -52,7 +49,7 @@ function serverConnection(socket: net.Socket) { suite('Debug - StreamDebugAdapter', () => { const port = rndPort(); - const pipeName = rndName(); + const pipeName = crypto.randomBytes(10).toString('utf8'); const pipePath = platform.isWindows ? join('\\\\.\\pipe\\', pipeName) : join(tmpdir(), pipeName); const testCases: { testName: string, debugAdapter: StreamDebugAdapter, connectionDetail: string | number }[] = [ From f8cf2fce66ff93bc1810c8403e33ee4a936ff6fb Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Thu, 23 Jul 2020 19:13:24 -0700 Subject: [PATCH 013/736] abstract more --- .../contrib/debug/node/debugAdapter.ts | 80 ++++++++----------- 1 file changed, 33 insertions(+), 47 deletions(-) diff --git a/src/vs/workbench/contrib/debug/node/debugAdapter.ts b/src/vs/workbench/contrib/debug/node/debugAdapter.ts index debe9451071..dc6d941a068 100644 --- a/src/vs/workbench/contrib/debug/node/debugAdapter.ts +++ b/src/vs/workbench/contrib/debug/node/debugAdapter.ts @@ -95,7 +95,35 @@ export abstract class NetworkDebugAdapter extends StreamDebugAdapter { protected socket?: net.Socket; - abstract startSession(): Promise; + protected abstract createConnection(connectionListener: () => void): net.Socket; + + startSession(): Promise { + return new Promise((resolve, reject) => { + let connected = false; + + this.socket = this.createConnection(() => { + this.connect(this.socket!, this.socket!); + resolve(); + connected = true; + }); + + this.socket.on('close', () => { + if (connected) { + this._onError.fire(new Error('connection closed')); + } else { + reject(new Error('connection closed')); + } + }); + + this.socket.on('error', error => { + if (connected) { + this._onError.fire(error); + } else { + reject(error); + } + }); + }); + } async stopSession(): Promise { await this.cancelPendingRequests(); @@ -115,29 +143,8 @@ export class SocketDebugAdapter extends NetworkDebugAdapter { super(); } - startSession(): Promise { - return new Promise((resolve, reject) => { - let connected = false; - this.socket = net.createConnection(this.adapterServer.port, this.adapterServer.host || '127.0.0.1', () => { - this.connect(this.socket!, this.socket!); - resolve(); - connected = true; - }); - this.socket.on('close', () => { - if (connected) { - this._onError.fire(new Error('connection closed')); - } else { - reject(new Error('connection closed')); - } - }); - this.socket.on('error', error => { - if (connected) { - this._onError.fire(error); - } else { - reject(error); - } - }); - }); + protected createConnection(connectionListener: () => void): net.Socket { + return net.createConnection(this.adapterServer.port, this.adapterServer.host || '127.0.0.1', connectionListener); } } @@ -150,29 +157,8 @@ export class NamedPipeDebugAdapter extends NetworkDebugAdapter { super(); } - startSession(): Promise { - return new Promise((resolve, reject) => { - let connected = false; - this.socket = net.createConnection(this.adapterServer.path, () => { - this.connect(this.socket!, this.socket!); - resolve(); - connected = true; - }); - this.socket.on('close', () => { - if (connected) { - this._onError.fire(new Error('connection closed')); - } else { - reject(new Error('connection closed')); - } - }); - this.socket.on('error', error => { - if (connected) { - this._onError.fire(error); - } else { - reject(error); - } - }); - }); + protected createConnection(connectionListener: () => void): net.Socket { + return net.createConnection(this.adapterServer.path, connectionListener); } } From c227faae4e4e9150c9f3c97922444d24667525f0 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Fri, 24 Jul 2020 13:45:47 -0700 Subject: [PATCH 014/736] add to descriptor --- src/vs/vscode.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 8df7d0ef548..5af2a0720ba 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -10843,7 +10843,7 @@ declare module 'vscode' { constructor(implementation: DebugAdapter); } - export type DebugAdapterDescriptor = DebugAdapterExecutable | DebugAdapterServer | DebugAdapterInlineImplementation; + export type DebugAdapterDescriptor = DebugAdapterExecutable | DebugAdapterServer | DebugAdapterNamedPipeServer | DebugAdapterInlineImplementation; export interface DebugAdapterDescriptorFactory { /** From df4b98fac594b0e1a9d5e93ad902dd4fc35521a2 Mon Sep 17 00:00:00 2001 From: Brian Malehorn Date: Sat, 25 Jul 2020 15:32:23 -0700 Subject: [PATCH 015/736] =?UTF-8?q?Running=20Extensions:=20sort=20slowest?= =?UTF-8?q?=20=E2=86=92=20fastest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before, extensions were sorted by: 1. whether or not they were unresponsive 2. name identifier This made it difficult to answer the question "which extensions are slowing me down"? Now, extensions are sorted by: 1. whether or not they were unresponsive 2. last profile time 3. activation time 4. name identifier In practice this means the first time you open running extensions, it will be sorted by activation time. When you profile extensions, it will be sorted by the profile time. --- .../runtimeExtensionsEditor.ts | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts index 1c780f7fb4f..0551286e0f3 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -233,11 +233,24 @@ export class RuntimeExtensionsEditor extends BaseEditor { result = result.filter(element => element.status.activationTimes); // bubble up extensions that have caused slowness + + const isUnresponsive = (extension: IRuntimeExtension): boolean => + extension.unresponsiveProfile === this._profileInfo; + + const profileTime = (extension: IRuntimeExtension): number => + extension.profileInfo?.totalTime ?? 0; + + const activationTime = (extension: IRuntimeExtension): number => + (extension.status.activationTimes?.codeLoadingTime ?? 0) + + (extension.status.activationTimes?.activateCallTime ?? 0); + result = result.sort((a, b) => { - if (a.unresponsiveProfile === this._profileInfo && !b.unresponsiveProfile) { - return -1; - } else if (!a.unresponsiveProfile && b.unresponsiveProfile === this._profileInfo) { - return 1; + if (isUnresponsive(a) || isUnresponsive(b)) { + return +isUnresponsive(b) - +isUnresponsive(a); + } else if (profileTime(a) || profileTime(b)) { + return profileTime(b) - profileTime(a); + } else if (activationTime(a) || activationTime(b)) { + return activationTime(b) - activationTime(a); } return a.originalIndex - b.originalIndex; }); From fd261ddba892f5177e1c50a5a74f530aed950011 Mon Sep 17 00:00:00 2001 From: nrayburn Date: Sun, 26 Jul 2020 00:25:21 -0500 Subject: [PATCH 016/736] Use Object.create(null) instead of {} for TokenRegistry --- src/vs/platform/theme/common/tokenClassificationRegistry.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/theme/common/tokenClassificationRegistry.ts b/src/vs/platform/theme/common/tokenClassificationRegistry.ts index f2fd8eb4264..e8bbd9403be 100644 --- a/src/vs/platform/theme/common/tokenClassificationRegistry.ts +++ b/src/vs/platform/theme/common/tokenClassificationRegistry.ts @@ -314,7 +314,7 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { constructor() { this.tokenTypeById = {}; this.tokenModifierById = {}; - this.typeHierarchy = {}; + this.typeHierarchy = Object.create(null); } public registerTokenType(id: string, description: string, superType?: string, deprecationMessage?: string): void { @@ -331,7 +331,7 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { const stylingSchemeEntry = getStylingSchemeEntry(description, deprecationMessage); this.tokenStylingSchema.properties[id] = stylingSchemeEntry; - this.typeHierarchy = {}; + this.typeHierarchy = Object.create(null); } public registerTokenModifier(id: string, description: string, deprecationMessage?: string): void { @@ -398,7 +398,7 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { public deregisterTokenType(id: string): void { delete this.tokenTypeById[id]; delete this.tokenStylingSchema.properties[id]; - this.typeHierarchy = {}; + this.typeHierarchy = Object.create(null); } public deregisterTokenModifier(id: string): void { From ec12042f3f8ef6d843ac1e586ac9b869ab880bf6 Mon Sep 17 00:00:00 2001 From: Dhananjay Tanpure Date: Sun, 26 Jul 2020 16:41:14 +0530 Subject: [PATCH 017/736] Activity bar can be at left or right of workbench --- src/vs/workbench/browser/workbench.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 88e1c72abb7..9ad5d13b7ee 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -402,7 +402,7 @@ import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuratio '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.") + 'description': nls.localize('zenMode.hideActivityBar', "Controls whether turning on Zen Mode also hides the activity bar either at the left or right of the workbench.") }, 'zenMode.hideLineNumbers': { 'type': 'boolean', From 7689b04dbea400a440174d3fcd8994c821e080dc Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 27 Jul 2020 10:00:58 -0700 Subject: [PATCH 018/736] notebook diff --- .../notebook/browser/contrib/scm/scm.ts | 130 ++++++++++++++++++ .../notebook/browser/media/notebook.css | 48 +++++++ .../notebook/browser/notebook.contribution.ts | 1 + .../notebook/browser/notebookBrowser.ts | 3 + .../notebook/browser/notebookEditorWidget.ts | 4 +- .../notebook/browser/view/notebookCellList.ts | 103 +++++++++++++- .../notebook/browser/view/notebookGutter.ts | 126 +++++++++++++++++ .../contrib/notebook/common/notebookCommon.ts | 4 +- .../notebook/test/testNotebookEditor.ts | 6 +- .../contrib/scm/browser/dirtydiffDecorator.ts | 2 +- 10 files changed, 420 insertions(+), 7 deletions(-) create mode 100644 src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts create mode 100644 src/vs/workbench/contrib/notebook/browser/view/notebookGutter.ts diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts b/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts new file mode 100644 index 00000000000..7166b1c2748 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts @@ -0,0 +1,130 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { INotebookEditorContribution, INotebookEditor, INotebookDeltaDecoration } from '../../notebookBrowser'; +import { registerNotebookContribution } from '../../notebookEditorExtensions'; +import { ISCMService } from 'vs/workbench/contrib/scm/common/scm'; +import { createProviderComparer } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; +import { first } from 'vs/base/common/async'; +import { INotebookService } from '../../../common/notebookService'; +import { NotebookCellTextModel } from '../../../common/model/notebookCellTextModel'; +import { diff } from '../../../common/notebookCommon'; + +export class SCMController extends Disposable implements INotebookEditorContribution { + static id: string = 'workbench.notebook.findController'; + private _lastDecorationId: string[] = []; + private _localDisposable = new DisposableStore(); + + + constructor( + private readonly _notebookEditor: INotebookEditor, + @ISCMService private readonly _scmService: ISCMService, + @INotebookService private readonly _notebookService: INotebookService + + ) { + super(); + + this._register(this._notebookEditor.onDidChangeModel(() => { + this._localDisposable.clear(); + this.update(); + + if (this._notebookEditor.textModel) { + this._localDisposable.add(this._notebookEditor.textModel.onDidChangeContent(() => { + this.update(); + })); + + this._localDisposable.add(this._notebookEditor.textModel.onDidChangeCells(() => { + this.update(); + })); + } + })); + + this.update(); + } + + async update() { + const modifiedDocument = this._notebookEditor.textModel; + if (!modifiedDocument) { + return; + } + + const uri = modifiedDocument.uri; + const providers = this._scmService.repositories.map(r => r.provider); + const rootedProviders = providers.filter(p => !!p.rootUri); + + rootedProviders.sort(createProviderComparer(uri)); + + const result = await first(rootedProviders.map(p => () => p.getOriginalResource(uri))); + + if (!result) { + this._clear(); + return; + } + + const originalDocument = await this._notebookService.resolveNotebook(modifiedDocument.viewType, result, false); + + if (!originalDocument) { + this._clear(); + return; + } + + // naive diff, runCode50 + // diff: 3.947998046875ms + // diff: 2.615966796875ms + + console.time('diff'); + + const cellDiffs = diff(originalDocument.cells, modifiedDocument.cells, a => { + for (let i = 0; i < originalDocument.cells.length; i++) { + const modifiedCell = originalDocument.cells[i]; + + if (modifiedCell.getValue() === a.getValue()) { + return true; + } + } + + return false; + }, (a, b) => { + return a.getValue() === b.getValue(); + }); + + console.timeEnd('diff'); + + const decorations: INotebookDeltaDecoration[] = []; + + cellDiffs.forEach(diff => { + if (diff.deleteCount === 0) { + // doesn't exist in original + // insert + decorations.push(...diff.toInsert.map(cell => ({ + handle: cell.handle, + options: { gutterClassName: 'nb-gutter-cell-inserted' } + }))); + } else { + if (diff.toInsert.length === 0) { + // diff.deleteCount + // removed from original + } else { + // modification + decorations.push(...diff.toInsert.map(cell => ({ + handle: cell.handle, + options: { gutterClassName: 'nb-gutter-cell-changed' } + }))); + } + } + }); + + + + this._lastDecorationId = this._notebookEditor.deltaCellDecorations(this._lastDecorationId, decorations); + } + + private _clear() { + this._lastDecorationId = this._notebookEditor.deltaCellDecorations(this._lastDecorationId, []); + } +} + +registerNotebookContribution(SCMController.id, SCMController); diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index ee6e7a0db93..61c9a0d427d 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -11,6 +11,48 @@ position: relative; } +.monaco-workbench .notebookOverlay .notebook-gutter { + position: absolute; + width: 6px; + top: 0px; + height: 100%; +} + +.monaco-workbench .notebookOverlay .notebook-gutter .monaco-list-row .cell { + visibility: hidden; + top: -8px; +} + +.monaco-workbench .notebookOverlay .notebook-gutter .monaco-list-row .cell.nb-gutter-cell-changed { + position: relative; + visibility: visible; + width: 2px; + transition: width 80ms linear, left 80ms linear; + background-color: #66afe0; +} + +.monaco-workbench .notebookOverlay .notebook-gutter .monaco-list-row:hover .cell.nb-gutter-cell-changed { + left: 0px; + width: 6px; +} + +.monaco-workbench .notebookOverlay .notebook-gutter .monaco-list-row .cell.nb-gutter-cell-inserted { + position: relative; + visibility: visible; + width: 2px; + transition: width 80ms linear, left 80ms linear; + background-color: #81b88b; +} + +.monaco-workbench .notebookOverlay .notebook-gutter .monaco-list-row:hover .cell.nb-gutter-cell-inserted { + left: 0px; + width: 6px; +} + +.monaco-workbench .notebookOverlay .notebook-gutter .monaco-scrollable-element > .scrollbar{ + visibility: hidden; +} + .monaco-workbench .cell.markdown { user-select: text; -webkit-user-select: text; @@ -55,6 +97,12 @@ width: 100%; } +.monaco-workbench .notebookOverlay > .cell-list-container > .notebook-gutter > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row { + cursor: default; + overflow: visible !important; + width: 100%; +} + .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image { position: absolute; top: -500px; diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index b73d6b5c81a..813fd052083 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -51,6 +51,7 @@ import 'vs/workbench/contrib/notebook/browser/contrib/format/formatting'; import 'vs/workbench/contrib/notebook/browser/contrib/toc/tocProvider'; import 'vs/workbench/contrib/notebook/browser/contrib/marker/markerProvider'; import 'vs/workbench/contrib/notebook/browser/contrib/status/editorStatus'; +import 'vs/workbench/contrib/notebook/browser/contrib/scm/scm'; // Output renderers registration diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 0ddd8114cda..9c34ea886b9 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -154,6 +154,7 @@ export interface INotebookEditorContribution { export interface INotebookCellDecorationOptions { className?: string; + gutterClassName?: string; outputClassName?: string; } @@ -375,6 +376,8 @@ export interface INotebookEditor extends IEditor { setCellSelection(cell: ICellViewModel, selection: Range): void; + deltaCellDecorations(oldDecorations: string[], newDecorations: INotebookDeltaDecoration[]): string[]; + /** * Change the decorations on cells. * The notebook is virtualized and this method should be called to create/delete editor decorations safely. diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 4cf404ef0e5..8890f7571f0 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -325,6 +325,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._list = this.instantiationService.createInstance( NotebookCellList, 'NotebookCellList', + this._overlayContainer, this._body, this.instantiationService.createInstance(NotebookCellListDelegate), renderers, @@ -1640,7 +1641,8 @@ export const cellSymbolHighlight = registerColor('notebook.symbolHighlightBackgr }, nls.localize('notebook.symbolHighlightBackground', "Background color of highlighted cell")); registerThemingParticipant((theme, collector) => { - collector.addRule(`.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element { + collector.addRule(`.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element, + .notebookOverlay > .cell-list-container > .notebook-gutter > .monaco-list > .monaco-scrollable-element { padding-top: ${SCROLLABLE_ELEMENT_PADDING_TOP}px; box-sizing: border-box; }`); diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index 249623eca74..f2b0b7bac48 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -3,10 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { IListRenderer, IListVirtualDelegate, ListError } from 'vs/base/browser/ui/list/list'; -import { IListStyles, IStyleController } from 'vs/base/browser/ui/list/listWidget'; +import { IListStyles, IStyleController, IListOptions } from 'vs/base/browser/ui/list/listWidget'; import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { isMacintosh } from 'vs/base/common/platform'; @@ -24,6 +25,9 @@ import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/ import { diff, IProcessedOutput, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { clamp } from 'vs/base/common/numbers'; import { SCROLLABLE_ELEMENT_PADDING_TOP } from 'vs/workbench/contrib/notebook/browser/constants'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { editorBackground, foreground, focusBorder } from 'vs/platform/theme/common/colorRegistry'; +import { NotebookGutter, NotebookGutterDelegate, GutterRenderer } from 'vs/workbench/contrib/notebook/browser/view/notebookGutter'; export interface IFocusNextPreviousDelegate { onFocusNext(applyFocusNext: () => void): void; @@ -62,8 +66,11 @@ export class NotebookCellList extends WorkbenchList implements ID private readonly _focusNextPreviousDelegate: IFocusNextPreviousDelegate; + private _cellListGutter: WorkbenchList; + constructor( private listUser: string, + parentContainer: HTMLElement, container: HTMLElement, delegate: IListVirtualDelegate, renderers: IListRenderer[], @@ -72,7 +79,8 @@ export class NotebookCellList extends WorkbenchList implements ID @IListService listService: IListService, @IThemeService themeService: IThemeService, @IConfigurationService configurationService: IConfigurationService, - @IKeybindingService keybindingService: IKeybindingService + @IKeybindingService keybindingService: IKeybindingService, + @IInstantiationService instantiationService: IInstantiationService ) { super(listUser, container, delegate, renderers, options, contextKeyService, listService, themeService, configurationService, keybindingService); NOTEBOOK_CELL_LIST_FOCUSED.bindTo(this.contextKeyService).set(true); @@ -150,6 +158,81 @@ export class NotebookCellList extends WorkbenchList implements ID focus.focusMode = CellFocusMode.Editor; } })); + + const gutterContainer = DOM.append(container, DOM.$('.notebook-gutter')); + + this._cellListGutter = instantiationService.createInstance( + NotebookGutter, + 'NotebookGutter', + gutterContainer, + instantiationService.createInstance(NotebookGutterDelegate), + [instantiationService.createInstance(GutterRenderer, (index, size) => { + DOM.scheduleAtNextAnimationFrame(() => { + this._cellListGutter?.updateElementHeight(index, size); + }); + })], + this.contextKeyService, + { + setRowLineHeight: false, + setRowHeight: false, + supportDynamicHeights: true, + horizontalScrolling: false, + keyboardSupport: false, + mouseSupport: false, + multipleSelectionSupport: false, + enableKeyboardNavigation: false, + additionalScrollHeight: 0, + transformOptimization: false, + styleController: (_suffix: string) => { return this; }, + overrideStyles: { + listBackground: editorBackground, + listActiveSelectionBackground: editorBackground, + listActiveSelectionForeground: foreground, + listFocusAndSelectionBackground: editorBackground, + listFocusAndSelectionForeground: foreground, + listFocusBackground: editorBackground, + listFocusForeground: foreground, + listHoverForeground: foreground, + listHoverBackground: editorBackground, + listHoverOutline: focusBorder, + listFocusOutline: focusBorder, + listInactiveSelectionBackground: editorBackground, + listInactiveSelectionForeground: foreground, + listInactiveFocusBackground: editorBackground, + listInactiveFocusOutline: editorBackground, + }, + accessibilityProvider: { + getAriaLabel() { return null; }, + getWidgetAriaLabel() { + return nls.localize('notebookTreeAriaLabel', "Notebook"); + } + } + }, + ); + + let scrolling = false; + + this._localDisposableStore.add(this.onWillScroll(() => { + scrolling = true; + })); + + this._localDisposableStore.add(this.onDidScroll(() => { + if (this._cellListGutter.scrollTop !== this.scrollTop) { + this._cellListGutter.scrollTop = this.scrollTop; + } + + scrolling = false; + })); + + this._localDisposableStore.add(this._cellListGutter.onDidScroll(() => { + if (scrolling) { + return; + } + + if (this._cellListGutter.scrollTop !== this.scrollTop) { + this.scrollTop = this._cellListGutter.scrollTop; + } + })); } elementAt(position: number): ICellViewModel | undefined { @@ -375,6 +458,8 @@ export class NotebookCellList extends WorkbenchList implements ID super.splice(start, deleteCount, elements); + this._cellListGutter.splice(start, deleteCount, elements); + const selectionsLeft = []; this._viewModel!.selectionHandles.forEach(handle => { if (this._viewModel!.hasCell(handle)) { @@ -563,6 +648,7 @@ export class NotebookCellList extends WorkbenchList implements ID const focused = this.getFocus(); this.view.updateElementHeight(index, size, focused.length ? focused[0] : null); + this._cellListGutter.updateElementHeight(index, size); } // override @@ -801,6 +887,19 @@ export class NotebookCellList extends WorkbenchList implements ID } } + updateOptions(options: IListOptions) { + super.updateOptions(options); + + if (options.additionalScrollHeight !== undefined) { + this._cellListGutter.updateOptions({ additionalScrollHeight: options.additionalScrollHeight }); + } + } + + layout(height?: number, width?: number): void { + super.layout(height, width); + this._cellListGutter.layout(height, width); + } + style(styles: IListStyles) { const selectorSuffix = this.view.domId; diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookGutter.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookGutter.ts new file mode 100644 index 00000000000..798975ef1e6 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookGutter.ts @@ -0,0 +1,126 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as DOM from 'vs/base/browser/dom'; +import { WorkbenchList, IListService, IWorkbenchListOptions } from 'vs/platform/list/browser/listService'; +import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { CellViewModel } from '../viewModel/notebookViewModel'; +import { DisposableStore } from 'vs/base/common/lifecycle'; + + +interface IGutterRendererTemplate { + container: HTMLElement; + cellContainer: HTMLElement; + elementDisposables: DisposableStore; +} + +export class GutterRenderer implements IListRenderer { + + static TEMPLATE_ID = 'notebook_gutter'; + + templateId = 'notebook_gutter'; + + constructor( + private _elementHeightUpdateDelegate: (index: number, size: number) => void + ) { + } + + renderTemplate(container: HTMLElement): IGutterRendererTemplate { + const cellContainer = DOM.append(container, DOM.$('.cell')); + + return { + container, + cellContainer, + elementDisposables: new DisposableStore() + }; + } + + renderElement(element: CellViewModel, index: number, templateData: IGutterRendererTemplate, height: number | undefined): void { + templateData.cellContainer.style.height = `${element.layoutInfo.totalHeight}px`; + let removedClassNames: string[] = []; + templateData.cellContainer.classList.forEach(className => { + if (/^nb\-.*$/.test(className)) { + removedClassNames.push(className); + } + }); + + removedClassNames.forEach(className => { + templateData.cellContainer.classList.remove(className); + }); + + templateData.elementDisposables.add(element.onDidChangeLayout(() => { + templateData.cellContainer.style.height = `${element.layoutInfo.totalHeight}px`; + this._elementHeightUpdateDelegate(index, element.layoutInfo.totalHeight); + })); + + element.getCellDecorations().forEach(options => { + if (options.gutterClassName) { + DOM.addClass(templateData.cellContainer, options.gutterClassName); + } + }); + + templateData.elementDisposables.add(element.onCellDecorationsChanged((e) => { + e.added.forEach(options => { + if (options.gutterClassName) { + DOM.addClass(templateData.cellContainer, options.gutterClassName); + } + }); + + e.removed.forEach(options => { + if (options.gutterClassName) { + DOM.removeClass(templateData.cellContainer, options.gutterClassName); + } + }); + })); + } + disposeTemplate(templateData: IGutterRendererTemplate): void { + templateData.cellContainer.style.backgroundColor = `#fff`; + + templateData.elementDisposables.clear(); + return; + } + + disposeElement(element: CellViewModel, index: number, templateData: IGutterRendererTemplate): void { + templateData.elementDisposables.clear(); + } +} + +export class NotebookGutterDelegate implements IListVirtualDelegate { + getHeight(element: CellViewModel): number { + return element.layoutInfo.totalHeight; + } + + hasDynamicHeight(element: CellViewModel): boolean { + return false; + } + + getTemplateId(element: CellViewModel): string { + return GutterRenderer.TEMPLATE_ID; + } +} + + +export class NotebookGutter extends WorkbenchList { + constructor( + listUser: string, + container: HTMLElement, + delegate: IListVirtualDelegate, + renderers: IListRenderer[], + contextKeyService: IContextKeyService, + options: IWorkbenchListOptions, + @IListService listService: IListService, + @IThemeService themeService: IThemeService, + @IConfigurationService configurationService: IConfigurationService, + @IKeybindingService keybindingService: IKeybindingService, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(listUser, container, delegate, renderers, options, contextKeyService, listService, themeService, configurationService, keybindingService); + } +} diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 6f459d81629..59899210746 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -507,7 +507,7 @@ interface IMutableSplice extends ISplice { deleteCount: number; } -export function diff(before: T[], after: T[], contains: (a: T) => boolean): ISplice[] { +export function diff(before: T[], after: T[], contains: (a: T) => boolean, equal: (a: T, b: T) => boolean = (a: T, b: T) => a === b): ISplice[] { const result: IMutableSplice[] = []; function pushSplice(start: number, deleteCount: number, toInsert: T[]): void { @@ -542,7 +542,7 @@ export function diff(before: T[], after: T[], contains: (a: T) => boolean): I const beforeElement = before[beforeIdx]; const afterElement = after[afterIdx]; - if (beforeElement === afterElement) { + if (equal(beforeElement, afterElement)) { // equal beforeIdx += 1; afterIdx += 1; diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index 5a5affa30d9..6f8b49a3cf5 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -12,7 +12,7 @@ import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { Range } from 'vs/editor/common/core/range'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { EditorModel } from 'vs/workbench/common/editor'; -import { ICellRange, ICellViewModel, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { ICellRange, ICellViewModel, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo, INotebookDeltaDecoration } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer'; import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { CellViewModel, IModelDecorationsChangeAccessor, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; @@ -258,6 +258,10 @@ export class TestNotebookEditor implements INotebookEditor { throw new Error('Method not implemented.'); } + deltaCellDecorations(oldDecorations: string[], newDecorations: INotebookDeltaDecoration[]): string[] { + throw new Error('Method not implemented.'); + } + deltaCellOutputContainerClassNames(cellId: string, added: string[], removed: string[]): void { throw new Error('Method not implemented.'); } diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index 450448112bb..77aa3ce26fb 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -982,7 +982,7 @@ function compareChanges(a: IChange, b: IChange): number { return a.originalEndLineNumber - b.originalEndLineNumber; } -function createProviderComparer(uri: URI): (a: ISCMProvider, b: ISCMProvider) => number { +export function createProviderComparer(uri: URI): (a: ISCMProvider, b: ISCMProvider) => number { return (a, b) => { const aIsParent = isEqualOrParent(uri, a.rootUri!); const bIsParent = isEqualOrParent(uri, b.rootUri!); From 4300f49e236c70fcd3f0f7f3803b6cf721906d50 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 27 Jul 2020 16:50:26 -0700 Subject: [PATCH 019/736] lcs diff --- src/vs/editor/common/model.ts | 1 + .../pieceTreeTextBuffer/pieceTreeBase.ts | 13 ++- .../pieceTreeTextBuffer.ts | 4 + .../notebook/browser/contrib/scm/scm.ts | 79 ++++++++++--------- .../notebook/browser/view/notebookCellList.ts | 18 +++-- .../common/model/notebookCellTextModel.ts | 16 ++++ .../common/model/notebookTextModel.ts | 3 + 7 files changed, 87 insertions(+), 47 deletions(-) diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index 56bb9bab50c..7272a53e6f0 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -1312,6 +1312,7 @@ export interface IReadonlyTextBuffer { getLinesContent(): string[]; getLineContent(lineNumber: number): string; getLineCharCode(lineNumber: number, index: number): number; + getCharCode(offset: number): number; getLineLength(lineNumber: number): number; getLineFirstNonWhitespaceColumn(lineNumber: number): number; getLineLastNonWhitespaceColumn(lineNumber: number): number; diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts index e0c7e80cfa6..03a518eb523 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts @@ -626,8 +626,7 @@ export class PieceTreeBase { return this._lastVisitedLine.value; } - public getLineCharCode(lineNumber: number, index: number): number { - let nodePos = this.nodeAt2(lineNumber, index + 1); + private _getCharCode(nodePos: NodePosition): number { if (nodePos.remainder === nodePos.node.piece.length) { // the char we want to fetch is at the head of next node. let matchingNode = nodePos.node.next(); @@ -647,6 +646,11 @@ export class PieceTreeBase { } } + public getLineCharCode(lineNumber: number, index: number): number { + let nodePos = this.nodeAt2(lineNumber, index + 1); + return this._getCharCode(nodePos); + } + public getLineLength(lineNumber: number): number { if (lineNumber === this.getLineCount()) { let startOffset = this.getOffsetAt(lineNumber, 1); @@ -655,6 +659,11 @@ export class PieceTreeBase { return this.getOffsetAt(lineNumber + 1, 1) - this.getOffsetAt(lineNumber, 1) - this._EOLLength; } + public getCharCode(offset: number): number { + let nodePos = this.nodeAt(offset); + return this._getCharCode(nodePos); + } + public findMatchesInNode(node: TreeNode, searcher: Searcher, startLineNumber: number, startColumn: number, startCursor: BufferCursor, endCursor: BufferCursor, searchData: SearchData, captureMatches: boolean, limitResultCount: number, resultLen: number, result: FindMatch[]) { let buffer = this._buffers[node.piece.bufferIndex]; let startOffsetInBuffer = this.offsetInBuffer(node.piece.bufferIndex, node.piece.start); diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts index 1c81c18a0a6..17d22590a6f 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts @@ -178,6 +178,10 @@ export class PieceTreeTextBuffer implements ITextBuffer, IDisposable { return this._pieceTree.getLineCharCode(lineNumber, index); } + public getCharCode(offset: number): number { + return this._pieceTree.getCharCode(offset); + } + public getLineLength(lineNumber: number): number { return this._pieceTree.getLineLength(lineNumber); } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts b/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts index 7166b1c2748..accd3d6007f 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts @@ -10,14 +10,31 @@ import { ISCMService } from 'vs/workbench/contrib/scm/common/scm'; import { createProviderComparer } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; import { first } from 'vs/base/common/async'; import { INotebookService } from '../../../common/notebookService'; -import { NotebookCellTextModel } from '../../../common/model/notebookCellTextModel'; -import { diff } from '../../../common/notebookCommon'; +import { ISequence, LcsDiff } from 'vs/base/common/diff/diff'; +import { NotebookTextModel } from '../../../common/model/notebookTextModel'; + +class CellSequence implements ISequence { + + constructor(readonly textModel: NotebookTextModel) { + } + + getElements(): string[] | number[] | Int32Array { + const hashValue = new Int32Array(this.textModel.cells.length); + for (let i = 0; i < this.textModel.cells.length; i++) { + hashValue[i] = this.textModel.cells[i].getHashValue(); + } + + return hashValue; + } +} export class SCMController extends Disposable implements INotebookEditorContribution { static id: string = 'workbench.notebook.findController'; private _lastDecorationId: string[] = []; private _localDisposable = new DisposableStore(); + private _lastVersion = -1; + constructor( private readonly _notebookEditor: INotebookEditor, @@ -51,6 +68,12 @@ export class SCMController extends Disposable implements INotebookEditorContribu return; } + if (this._lastVersion >= modifiedDocument.versionId) { + return; + } + + this._lastVersion = modifiedDocument.versionId; + const uri = modifiedDocument.uri; const providers = this._scmService.repositories.map(r => r.provider); const rootedProviders = providers.filter(p => !!p.rootUri); @@ -71,54 +94,36 @@ export class SCMController extends Disposable implements INotebookEditorContribu return; } - // naive diff, runCode50 - // diff: 3.947998046875ms - // diff: 2.615966796875ms - - console.time('diff'); - - const cellDiffs = diff(originalDocument.cells, modifiedDocument.cells, a => { - for (let i = 0; i < originalDocument.cells.length; i++) { - const modifiedCell = originalDocument.cells[i]; - - if (modifiedCell.getValue() === a.getValue()) { - return true; - } - } - - return false; - }, (a, b) => { - return a.getValue() === b.getValue(); - }); - - console.timeEnd('diff'); + const diff = new LcsDiff(new CellSequence(originalDocument), new CellSequence(modifiedDocument)); + const diffResult = diff.ComputeDiff(false); const decorations: INotebookDeltaDecoration[] = []; - - cellDiffs.forEach(diff => { - if (diff.deleteCount === 0) { + diffResult.changes.forEach(change => { + if (change.originalLength === 0) { // doesn't exist in original - // insert - decorations.push(...diff.toInsert.map(cell => ({ - handle: cell.handle, - options: { gutterClassName: 'nb-gutter-cell-inserted' } - }))); + for (let i = 0; i < change.modifiedLength; i++) { + decorations.push({ + handle: modifiedDocument.cells[change.modifiedStart + i].handle, + options: { gutterClassName: 'nb-gutter-cell-inserted' } + }); + } } else { - if (diff.toInsert.length === 0) { + if (change.modifiedLength === 0) { // diff.deleteCount // removed from original } else { // modification - decorations.push(...diff.toInsert.map(cell => ({ - handle: cell.handle, - options: { gutterClassName: 'nb-gutter-cell-changed' } - }))); + for (let i = 0; i < change.modifiedLength; i++) { + decorations.push({ + handle: modifiedDocument.cells[change.modifiedStart + i].handle, + options: { gutterClassName: 'nb-gutter-cell-changed' } + }); + } } } }); - this._lastDecorationId = this._notebookEditor.deltaCellDecorations(this._lastDecorationId, decorations); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index f2b0b7bac48..1a804a736ff 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -224,15 +224,17 @@ export class NotebookCellList extends WorkbenchList implements ID scrolling = false; })); - this._localDisposableStore.add(this._cellListGutter.onDidScroll(() => { - if (scrolling) { - return; - } + //TODO - if (this._cellListGutter.scrollTop !== this.scrollTop) { - this.scrollTop = this._cellListGutter.scrollTop; - } - })); + // this._localDisposableStore.add(this._cellListGutter.onDidScroll(() => { + // if (scrolling) { + // return; + // } + + // if (this._cellListGutter.scrollTop !== this.scrollTop) { + // this.scrollTop = this._cellListGutter.scrollTop; + // } + // })); } elementAt(position: number): ICellViewModel | undefined { diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts index 015b9db73be..c26a4856557 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts @@ -11,6 +11,8 @@ import * as model from 'vs/editor/common/model'; import { Range } from 'vs/editor/common/core/range'; import { Disposable } from 'vs/base/common/lifecycle'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { hash } from 'vs/base/common/hash'; + export class NotebookCellTextModel extends Disposable implements ICell { private _onDidChangeOutputs = new Emitter(); @@ -39,6 +41,7 @@ export class NotebookCellTextModel extends Disposable implements ICell { set metadata(newMetadata: NotebookCellMetadata | undefined) { this._metadata = newMetadata; + this._hash = null; this._onDidChangeMetadata.fire(); } @@ -48,6 +51,7 @@ export class NotebookCellTextModel extends Disposable implements ICell { set language(newLanguage: string) { this._language = newLanguage; + this._hash = null; this._onDidChangeLanguage.fire(newLanguage); } @@ -64,12 +68,15 @@ export class NotebookCellTextModel extends Disposable implements ICell { this._textBuffer = bufferFactory.create(model.DefaultEndOfLine.LF); this._register(this._textBuffer.onDidChangeContent(() => { + this._hash = null; this._onDidChangeContent.fire(); })); return this._textBuffer; } + private _hash: number | null = null; + constructor( readonly uri: URI, @@ -96,6 +103,15 @@ export class NotebookCellTextModel extends Disposable implements ICell { } } + getHashValue(): number { + if (this._hash !== null) { + return this._hash; + } + + this._hash = hash([hash(this.getValue()), this._metadata]); + return this._hash; + } + getTextLength(): number { return this.textBuffer.getLength(); } diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 702d445cc32..5946b3430d7 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -214,6 +214,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._mapping.set(mainCells[i].handle, mainCells[i]); let dirtyStateListener = mainCells[i].onDidChangeContent(() => { this.setDirty(true); + this._increaseVersionId(); this._onDidChangeContent.fire(); }); @@ -387,6 +388,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel let dirtyStateListener = cell.onDidChangeContent(() => { this._isUntitled = false; this.setDirty(true); + this._increaseVersionId(); this._onDidChangeContent.fire(); }); @@ -423,6 +425,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._mapping.set(cells[i].handle, cells[i]); let dirtyStateListener = cells[i].onDidChangeContent(() => { this.setDirty(true); + this._increaseVersionId(); this._onDidChangeContent.fire(); }); From 3994e92d88ea5e08548f3a29315e08fffec5191a Mon Sep 17 00:00:00 2001 From: Ciprian Flroescu Date: Tue, 28 Jul 2020 15:07:38 +0300 Subject: [PATCH 020/736] Make mtk classes more specific to avoid CSS conflicts --- src/vs/editor/common/modes/supports/tokenization.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/common/modes/supports/tokenization.ts b/src/vs/editor/common/modes/supports/tokenization.ts index 12566c3b00e..721c41f9fc0 100644 --- a/src/vs/editor/common/modes/supports/tokenization.ts +++ b/src/vs/editor/common/modes/supports/tokenization.ts @@ -397,12 +397,13 @@ export class ThemeTrieElement { export function generateTokensCSSForColorMap(colorMap: Color[]): string { let rules: string[] = []; + const editorParentClass = ".monaco-editor"; for (let i = 1, len = colorMap.length; i < len; i++) { let color = colorMap[i]; - rules[i] = `.mtk${i} { color: ${color}; }`; + rules[i] = `${editorParentClass} .mtk${i} { color: ${color}; }`; } - rules.push('.mtki { font-style: italic; }'); - rules.push('.mtkb { font-weight: bold; }'); - rules.push('.mtku { text-decoration: underline; text-underline-position: under; }'); + rules.push(`${editorParentClass} .mtki { font-style: italic; }`); + rules.push(`${editorParentClass} .mtkb { font-weight: bold; }`); + rules.push(`${editorParentClass} .mtku { text-decoration: underline; text-underline-position: under; }`); return rules.join('\n'); } From 51fddb1e914765b4bf6644330b98f629921f936e Mon Sep 17 00:00:00 2001 From: Ciprian Flroescu Date: Tue, 28 Jul 2020 16:20:18 +0300 Subject: [PATCH 021/736] Replaced double quotes with single quotes --- src/vs/editor/common/modes/supports/tokenization.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/common/modes/supports/tokenization.ts b/src/vs/editor/common/modes/supports/tokenization.ts index 721c41f9fc0..8cade505f3a 100644 --- a/src/vs/editor/common/modes/supports/tokenization.ts +++ b/src/vs/editor/common/modes/supports/tokenization.ts @@ -397,7 +397,7 @@ export class ThemeTrieElement { export function generateTokensCSSForColorMap(colorMap: Color[]): string { let rules: string[] = []; - const editorParentClass = ".monaco-editor"; + const editorParentClass = '.monaco-editor'; for (let i = 1, len = colorMap.length; i < len; i++) { let color = colorMap[i]; rules[i] = `${editorParentClass} .mtk${i} { color: ${color}; }`; From 0679eda1ee0d6140ac42243359cb0271cad20f9f Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 28 Jul 2020 10:34:21 -0700 Subject: [PATCH 022/736] diff and color decorations --- .../notebook/browser/contrib/scm/scm.ts | 52 ++-- .../notebook/browser/media/notebookDiff.css | 17 ++ .../notebook/browser/notebook.contribution.ts | 63 ++++- .../notebook/browser/notebookBrowser.ts | 7 +- .../notebook/browser/notebookDiffEditor.ts | 177 ++++++++++++++ .../browser/notebookDiffEditorInput.ts | 229 ++++++++++++++++++ .../notebook/browser/notebookEditorWidget.ts | 45 +++- .../browser/notebookEditorWidgetService.ts | 2 +- .../contrib/notebook/common/notebookCommon.ts | 22 ++ 9 files changed, 577 insertions(+), 37 deletions(-) create mode 100644 src/vs/workbench/contrib/notebook/browser/media/notebookDiff.css create mode 100644 src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts create mode 100644 src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts b/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts index accd3d6007f..4faa80fd842 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts @@ -10,23 +10,8 @@ import { ISCMService } from 'vs/workbench/contrib/scm/common/scm'; import { createProviderComparer } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; import { first } from 'vs/base/common/async'; import { INotebookService } from '../../../common/notebookService'; -import { ISequence, LcsDiff } from 'vs/base/common/diff/diff'; -import { NotebookTextModel } from '../../../common/model/notebookTextModel'; - -class CellSequence implements ISequence { - - constructor(readonly textModel: NotebookTextModel) { - } - - getElements(): string[] | number[] | Int32Array { - const hashValue = new Int32Array(this.textModel.cells.length); - for (let i = 0; i < this.textModel.cells.length; i++) { - hashValue[i] = this.textModel.cells[i].getHashValue(); - } - - return hashValue; - } -} +import { LcsDiff } from 'vs/base/common/diff/diff'; +import { CellSequence } from 'vs/workbench/contrib/notebook/common/notebookCommon'; export class SCMController extends Disposable implements INotebookEditorContribution { static id: string = 'workbench.notebook.findController'; @@ -44,25 +29,28 @@ export class SCMController extends Disposable implements INotebookEditorContribu ) { super(); - this._register(this._notebookEditor.onDidChangeModel(() => { - this._localDisposable.clear(); + if (!this._notebookEditor.creationOptions.isEmbeded) { + this._register(this._notebookEditor.onDidChangeModel(() => { + this._localDisposable.clear(); + this.update(); + + if (this._notebookEditor.textModel) { + this._localDisposable.add(this._notebookEditor.textModel.onDidChangeContent(() => { + this.update(); + })); + + this._localDisposable.add(this._notebookEditor.textModel.onDidChangeCells(() => { + this.update(); + })); + } + })); + this.update(); - - if (this._notebookEditor.textModel) { - this._localDisposable.add(this._notebookEditor.textModel.onDidChangeContent(() => { - this.update(); - })); - - this._localDisposable.add(this._notebookEditor.textModel.onDidChangeCells(() => { - this.update(); - })); - } - })); - - this.update(); + } } async update() { + const modifiedDocument = this._notebookEditor.textModel; if (!modifiedDocument) { return; diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebookDiff.css b/src/vs/workbench/contrib/notebook/browser/media/notebookDiff.css new file mode 100644 index 00000000000..80404d79179 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/media/notebookDiff.css @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.notebook-diff-editor { + display: flex; + flex-direction: row; + height: 100%; + width: 100%; +} +.notebook-diff-editor-modified, +.notebook-diff-editor-original { + display: flex; + height: 100%; + width: 50%; +} diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 813fd052083..48fd33b7022 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -41,6 +41,10 @@ import { NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/not import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { INotebookEditorModelResolverService, NotebookModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; +import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; +import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; +import { NotebookDiffEditorInput } from './notebookDiffEditorInput'; +import { NotebookDiffEditor } from './notebookDiffEditor'; // Editor Contribution @@ -58,7 +62,6 @@ import 'vs/workbench/contrib/notebook/browser/contrib/scm/scm'; import 'vs/workbench/contrib/notebook/browser/view/output/transforms/streamTransform'; import 'vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform'; import 'vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform'; -import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; /*--------------------------------------------------------------------------------------------- */ @@ -73,6 +76,17 @@ Registry.as(EditorExtensions.Editors).registerEditor( ] ); +Registry.as(EditorExtensions.Editors).registerEditor( + EditorDescriptor.create( + NotebookDiffEditor, + NotebookDiffEditor.ID, + 'Notebook Diff Editor' + ), + [ + new SyncDescriptor(NotebookDiffEditorInput) + ] +); + class NotebookEditorFactory implements IEditorInputFactory { canSerialize(): boolean { return true; @@ -232,6 +246,10 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri return undefined; } + if (originalInput instanceof DiffEditorInput) { + return this._handleDiffEditorInput(originalInput, options, group); + } + if (!originalInput.resource) { return undefined; } @@ -307,6 +325,49 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri const notebookOptions = new NotebookEditorOptions({ ...options, cellOptions, override: false, index }); return { override: this.editorService.openEditor(notebookInput, notebookOptions, group) }; } + + private _handleDiffEditorInput(diffEditorInput: DiffEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup): IOpenEditorOverride | undefined { + const modifiedInput = diffEditorInput.modifiedInput; + const originalInput = diffEditorInput.originalInput; + const notebookUri = modifiedInput.resource; + const originalNotebookUri = originalInput.resource; + + if (!notebookUri || !originalNotebookUri) { + return undefined; + } + + const existingEditors = group.editors.filter(editor => editor.resource && isEqual(editor.resource, notebookUri) && !(editor instanceof NotebookEditorInput)); + + if (existingEditors.length) { + return undefined; + } + + const userAssociatedEditors = this.getUserAssociatedEditors(notebookUri); + const notebookEditor = userAssociatedEditors.filter(association => this.notebookService.getContributedNotebookProvider(association.viewType)); + + if (userAssociatedEditors.length && !notebookEditor.length) { + // user pick a non-notebook editor for this resource + return undefined; + } + + // user might pick a notebook editor + + const associatedEditors = distinct([ + ...this.getUserAssociatedNotebookEditors(notebookUri), + ...(this.getContributedEditors(notebookUri).filter(editor => editor.priority === NotebookEditorPriority.default)) + ], editor => editor.id); + + if (!associatedEditors.length) { + // there is no notebook editor contribution which is enabled by default + return undefined; + } + + const info = associatedEditors[0]; + + const notebookInput = NotebookDiffEditorInput.create(this.instantiationService, notebookUri, modifiedInput.getName(), originalNotebookUri, originalInput.getName(), info.id); + const notebookOptions = new NotebookEditorOptions({ ...options, override: false }); + return { override: this.editorService.openEditor(notebookInput, notebookOptions, group) }; + } } class CellContentProvider implements ITextModelContentProvider { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 9c34ea886b9..2f10e1a1a71 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -163,6 +163,10 @@ export interface INotebookDeltaDecoration { options: INotebookCellDecorationOptions; } +export interface INotebookEditorCreationOptions { + readonly isEmbeded: boolean; +} + export interface INotebookEditor extends IEditor { cursorNavigationMode: boolean; @@ -178,7 +182,8 @@ export interface INotebookEditor extends IEditor { */ readonly onDidChangeModel: Event; readonly onDidFocusEditorWidget: Event; - isNotebookEditor: boolean; + readonly isNotebookEditor: boolean; + readonly creationOptions: INotebookEditorCreationOptions; activeKernel: INotebookKernelInfo | INotebookKernelInfo2 | undefined; multipleKernelsAvailable: boolean; readonly onDidChangeAvailableKernels: Event; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts new file mode 100644 index 00000000000..5ac0f937ecd --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts @@ -0,0 +1,177 @@ +/*--------------------------------------------------------------------------------------------- + * 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/notebook'; +import 'vs/css!./media/notebookDiff'; +import * as DOM from 'vs/base/browser/dom'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorOptions } from 'vs/workbench/common/editor'; +import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; +import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { NotebookDiffEditorInput } from './notebookDiffEditorInput'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IDiffResult, LcsDiff } from 'vs/base/common/diff/diff'; +import { CellSequence } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookDeltaDecoration } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; + + +export class NotebookDiffEditor extends BaseEditor { + static readonly ID: string = 'workbench.editor.notebookDiffEditor'; + + private _rootElement!: HTMLElement; + private _originalElement!: HTMLElement; + private _modifiedElement!: HTMLElement; + private _dimension?: DOM.Dimension; + private _widget: NotebookEditorWidget | null = null; + private _originalWidget: NotebookEditorWidget | null = null; + private _cellDecorations: string[] = []; + private _originalCellDecorations: string[] = []; + + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IThemeService themeService: IThemeService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IStorageService storageService: IStorageService, + ) { + super(NotebookDiffEditor.ID, telemetryService, themeService, storageService); + } + + protected createEditor(parent: HTMLElement): void { + this._rootElement = DOM.append(parent, DOM.$('.notebook-diff-editor')); + this._originalElement = DOM.append(this._rootElement, DOM.$('.notebook-diff-editor-original')); + this._modifiedElement = DOM.append(this._rootElement, DOM.$('.notebook-diff-editor-modified')); + } + + async setInput(input: NotebookDiffEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { + // const group = this.group!; + + await super.setInput(input, options, token); + this._widget = this.instantiationService.createInstance(NotebookEditorWidget, { isEmbeded: true }); + this._widget.createEditor(); + + this._originalWidget = this.instantiationService.createInstance(NotebookEditorWidget, { isEmbeded: true }); + this._originalWidget.createEditor(); + + if (this._dimension) { + this._widget.layout({ + width: this._dimension.width / 2, + height: this._dimension.height + }, this._modifiedElement); + + this._originalWidget.layout({ + width: this._dimension.width / 2, + height: this._dimension.height + }, this._originalElement); + } + + const model = await input.resolve(this._widget.getId()); + + if (model === null) { + return; + } + + await this._widget.setModel(model.modified.notebook, undefined); + await this._originalWidget.setModel(model.original.notebook, undefined); + + this._register(this._widget.onWillScroll(e => { + if (this._originalWidget) { + this._originalWidget.scrollTop = e.scrollTop; + } + })); + + this._register(this._originalWidget.onWillScroll(e => { + if (this._widget) { + this._widget.scrollTop = e.scrollTop; + } + })); + + const diff = new LcsDiff(new CellSequence(model.original.notebook), new CellSequence(model.modified.notebook)); + const diffResult = diff.ComputeDiff(false); + + this._adjustHeight(diffResult); + } + + private _adjustHeight(diffResult: IDiffResult) { + if (!this._widget || !this._originalWidget) { + return; + } + + const originalDecorations: INotebookDeltaDecoration[] = []; + const modifiedDecorations: INotebookDeltaDecoration[] = []; + diffResult.changes.forEach(change => { + const original = this._originalWidget?.textModel?.cells.slice(change.originalStart, change.originalStart + change.originalLength) + .map(cell => cell.handle).map(handle => ({ + handle: handle, + options: { className: 'nb-cell-deleted' } + })) || []; + + const modified = this._widget?.textModel?.cells.slice(change.modifiedStart, change.modifiedStart + change.modifiedLength) + .map(cell => cell.handle).map(handle => ({ + handle: handle, + options: { className: 'nb-cell-added' } + })) || []; + + originalDecorations.push(...original); + modifiedDecorations.push(...modified); + }); + + this._originalCellDecorations = this._originalWidget.deltaCellDecorations(this._originalCellDecorations, originalDecorations); + this._cellDecorations = this._widget.deltaCellDecorations(this._cellDecorations, modifiedDecorations); + } + + getDomNode() { + return this._rootElement; + } + + getControl(): NotebookEditorWidget | undefined { + return this._widget || undefined; + } + + setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void { + super.setEditorVisible(visible, group); + if (!visible) { + if (this.input && this._widget) { + // the widget is not transfered to other editor inputs + this._widget.onWillHide(); + } + } + + } + + focus() { + super.focus(); + this._widget?.focus(); + } + + + clearInput(): void { + if (this._widget) { + this._widget.onWillHide(); + } + super.clearInput(); + } + + layout(dimension: DOM.Dimension): void { + this._rootElement.classList.toggle('mid-width', dimension.width < 1000 && dimension.width >= 600); + this._rootElement.classList.toggle('narrow-width', dimension.width < 600); + this._dimension = dimension; + + this._widget?.layout({ + width: this._dimension.width / 2, + height: this._dimension.height + }, this._modifiedElement); + + this._originalWidget?.layout({ + width: this._dimension.width / 2, + height: this._dimension.height + }, this._originalElement); + } + +} diff --git a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts new file mode 100644 index 00000000000..5a001605257 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts @@ -0,0 +1,229 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { EditorInput, IEditorInput, GroupIdentifier, ISaveOptions, IMoveResult, IRevertOptions, EditorModel } from 'vs/workbench/common/editor'; +import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; +import { URI } from 'vs/base/common/uri'; +import { isEqual } from 'vs/base/common/resources'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; +import { IReference } from 'vs/base/common/lifecycle'; +import { INotebookEditorModel, INotebookDiffEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookEditorModel } from '../common/notebookEditorModel'; +import { IEditorModel } from 'vs/editor/common/editorCommon'; + +interface NotebookEditorInputOptions { + startDirty?: boolean; +} + +class NotebookDiffEditorModel extends EditorModel implements INotebookDiffEditorModel { + constructor( + readonly original: INotebookEditorModel, + readonly modified: INotebookEditorModel, + ) { + super(); + } + + async load(): Promise { + await this.original.load(); + await this.modified.load(); + + return this; + } + + dispose(): void { + + } + +} + +export class NotebookDiffEditorInput extends EditorInput { + static create(instantiationService: IInstantiationService, resource: URI, name: string, originalResource: URI, originalName: string, viewType: string | undefined, options: NotebookEditorInputOptions = {}) { + return instantiationService.createInstance(NotebookDiffEditorInput, resource, name, originalResource, originalName, viewType, options); + } + + static readonly ID: string = 'workbench.input.diffNotebookInput'; + + private _textModel: IReference | null = null; + private _originalTextModel: IReference | null = null; + private _defaultDirtyState: boolean = false; + + constructor( + public readonly resource: URI, + public readonly name: string, + public readonly originalResource: URI, + public readonly originalName: string, + public readonly viewType: string | undefined, + public readonly options: NotebookEditorInputOptions, + @INotebookService private readonly _notebookService: INotebookService, + @INotebookEditorModelResolverService private readonly _notebookModelResolverService: INotebookEditorModelResolverService, + @IFilesConfigurationService private readonly _filesConfigurationService: IFilesConfigurationService, + @IFileDialogService private readonly _fileDialogService: IFileDialogService, + // @IInstantiationService private readonly _instantiationService: IInstantiationService + ) { + super(); + this._defaultDirtyState = !!options.startDirty; + } + + getTypeId(): string { + return NotebookDiffEditorInput.ID; + } + + getName(): string { + return nls.localize('sideBySideLabels', "{0} ↔ {1}", this.originalName, this.name); + } + + isDirty() { + if (!this._textModel) { + return !!this._defaultDirtyState; + } + return this._textModel.object.isDirty(); + } + + isUntitled(): boolean { + return this._textModel?.object.isUntitled() || false; + } + + isReadonly() { + return false; + } + + isSaving(): boolean { + if (this.isUntitled()) { + return false; // untitled is never saving automatically + } + + if (!this.isDirty()) { + return false; // the editor needs to be dirty for being saved + } + + if (this._filesConfigurationService.getAutoSaveMode() === AutoSaveMode.AFTER_SHORT_DELAY) { + return true; // a short auto save is configured, treat this as being saved + } + + return false; + } + + async save(group: GroupIdentifier, options?: ISaveOptions): Promise { + if (this._textModel) { + + if (this.isUntitled()) { + return this.saveAs(group, options); + } else { + await this._textModel.object.save(); + } + + return this; + } + + return undefined; + } + + async saveAs(group: GroupIdentifier, options?: ISaveOptions): Promise { + if (!this._textModel || !this.viewType) { + return undefined; + } + + const provider = this._notebookService.getContributedNotebookProvider(this.viewType!); + + if (!provider) { + return undefined; + } + + const dialogPath = this._textModel.object.resource; + const target = await this._fileDialogService.pickFileToSave(dialogPath, options?.availableFileSystems); + if (!target) { + return undefined; // save cancelled + } + + if (!provider.matches(target)) { + const patterns = provider.selector.map(pattern => { + if (pattern.excludeFileNamePattern) { + return `${pattern.filenamePattern} (exclude: ${pattern.excludeFileNamePattern})`; + } + + return pattern.filenamePattern; + }).join(', '); + throw new Error(`File name ${target} is not supported by ${provider.providerDisplayName}. + +Please make sure the file name matches following patterns: +${patterns} +`); + } + + if (!await this._textModel.object.saveAs(target)) { + return undefined; + } + + return this._move(group, target)?.editor; + } + + // called when users rename a notebook document + rename(group: GroupIdentifier, target: URI): IMoveResult | undefined { + if (this._textModel) { + const contributedNotebookProviders = this._notebookService.getContributedNotebookProviders(target); + + if (contributedNotebookProviders.find(provider => provider.id === this._textModel!.object.viewType)) { + return this._move(group, target); + } + } + return undefined; + } + + private _move(group: GroupIdentifier, newResource: URI): { editor: IEditorInput } | undefined { + return undefined; + } + + async revert(group: GroupIdentifier, options?: IRevertOptions): Promise { + if (this._textModel && this._textModel.object.isDirty()) { + await this._textModel.object.revert(options); + } + + return; + } + + async resolve(editorId?: string): Promise { + if (!await this._notebookService.canResolve(this.viewType!)) { + return null; + } + + if (!this._textModel) { + this._textModel = await this._notebookModelResolverService.resolve(this.resource, this.viewType!, editorId); + this._originalTextModel = await this._notebookModelResolverService.resolve(this.originalResource, this.viewType!, editorId); + + // this._register(this._textModel.object.onDidChangeDirty(() => { + // this._onDidChangeDirty.fire(); + // })); + + // if (this._textModel.object.isDirty()) { + // this._onDidChangeDirty.fire(); + // } + } + + return new NotebookDiffEditorModel(this._originalTextModel!.object, this._textModel.object); + } + + matches(otherInput: unknown): boolean { + if (this === otherInput) { + return true; + } + if (otherInput instanceof NotebookDiffEditorInput) { + return this.viewType === otherInput.viewType + && isEqual(this.resource, otherInput.resource); + } + return false; + } + + dispose() { + if (this._textModel) { + this._textModel.dispose(); + this._textModel = null; + } + super.dispose(); + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 8890f7571f0..365cb52632e 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -23,12 +23,12 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { contrastBorder, editorBackground, focusBorder, foreground, registerColor, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground, textPreformatForeground, errorForeground, transparent, listFocusBackground, listInactiveSelectionBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, scrollbarSliderActiveBackground } from 'vs/platform/theme/common/colorRegistry'; +import { contrastBorder, editorBackground, focusBorder, foreground, registerColor, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground, textPreformatForeground, errorForeground, transparent, listFocusBackground, listInactiveSelectionBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, scrollbarSliderActiveBackground, diffRemoved, diffInserted } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { EditorMemento } from 'vs/workbench/browser/parts/editor/baseEditor'; import { EditorOptions, IEditorMemento } from 'vs/workbench/common/editor'; import { CELL_MARGIN, CELL_RUN_GUTTER, EDITOR_BOTTOM_PADDING, EDITOR_TOP_MARGIN, EDITOR_TOP_PADDING, SCROLLABLE_ELEMENT_PADDING_TOP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellEditState, CellFocusMode, ICellRange, ICellViewModel, INotebookCellList, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_OUTPUT_FOCUSED, INotebookDeltaDecoration } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellEditState, CellFocusMode, ICellRange, ICellViewModel, INotebookCellList, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_OUTPUT_FOCUSED, INotebookDeltaDecoration, INotebookEditorCreationOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList'; import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer'; @@ -51,6 +51,8 @@ import { debugIconStartForeground } from 'vs/workbench/contrib/debug/browser/deb import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys'; import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { notebookKernelProviderAssociationsSettingId, NotebookKernelProviderAssociations } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation'; +import { ScrollEvent } from 'vs/base/common/scrollable'; +import { editorGutterAddedBackground, editorGutterDeletedBackground, editorGutterModifiedBackground } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; const $ = DOM.$; @@ -103,6 +105,15 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private readonly _activeKernelMemento: Memento; private readonly _onDidFocusEmitter = this._register(new Emitter()); public readonly onDidFocus = this._onDidFocusEmitter.event; + private readonly _onWillScroll = this._register(new Emitter()); + public readonly onWillScroll: Event = this._onWillScroll.event; + + set scrollTop(top: number) { + if (this._list) { + this._list.scrollTop = top; + } + } + private _cellContextKeyManager: CellContextKeyManager | null = null; private _isVisible = false; private readonly _uuid = generateUuid(); @@ -194,6 +205,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } constructor( + readonly creationOptions: INotebookEditorCreationOptions, @IInstantiationService private readonly instantiationService: IInstantiationService, @IStorageService storageService: IStorageService, @INotebookService private notebookService: INotebookService, @@ -789,6 +801,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } this._localStore.add(this._list!.onWillScroll(e => { + this._onWillScroll.fire(e); if (!this._webviewResolved) { return; } @@ -1786,6 +1799,34 @@ registerThemingParticipant((theme, collector) => { collector.addRule(` .notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .scrollbar > .slider.active:before { content: ""; width: 100%; height: 100%; position: absolute; background: ${scrollbarSliderActiveBackgroundColor}; } `); /* hack to not have cells see through scroller */ } + // case ChangeType.Modify: return theme.getColor(editorGutterModifiedBackground); + // case ChangeType.Add: return theme.getColor(editorGutterAddedBackground); + // case ChangeType.Delete: return theme.getColor(editorGutterDeletedBackground); + // diff + + const modifiedBackground = theme.getColor(editorGutterModifiedBackground); + if (modifiedBackground) { + collector.addRule(`.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.nb-cell-modified .cell-focus-indicator, + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.nb-cell-modified.markdown-cell-row { + background-color: ${modifiedBackground} !important; + }`); + } + + const addedBackground = theme.getColor(diffInserted); + if (addedBackground) { + collector.addRule(`.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.nb-cell-added .cell-focus-indicator, + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.nb-cell-added.markdown-cell-row { + background-color: ${addedBackground} !important; + }`); + } + const deletedBackground = theme.getColor(diffRemoved); + if (deletedBackground) { + collector.addRule(`.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.nb-cell-deleted .cell-focus-indicator, + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.nb-cell-deleted.markdown-cell-row { + background-color: ${deletedBackground} !important; + }`); + } + // Cell Margin collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > div.cell { margin: 0px ${CELL_MARGIN * 2}px 0px ${CELL_MARGIN}px; }`); collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > div.cell.code { margin-left: ${CODE_CELL_LEFT_MARGIN}px; }`); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidgetService.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidgetService.ts index 2369eee6a02..dcccacee583 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidgetService.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidgetService.ts @@ -126,7 +126,7 @@ class NotebookEditorWidgetService implements INotebookEditorWidgetService { if (!value) { // NEW widget const instantiationService = accessor.get(IInstantiationService); - const widget = instantiationService.createInstance(NotebookEditorWidget); + const widget = instantiationService.createInstance(NotebookEditorWidget, { isEmbeded: false }); widget.createEditor(); const token = this._tokenPool++; value = { widget, token }; diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 59899210746..87eaa7da738 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -18,6 +18,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Schemas } from 'vs/base/common/network'; import { IRevertOptions } from 'vs/workbench/common/editor'; import { basename } from 'vs/base/common/path'; +import { ISequence } from 'vs/base/common/diff/diff'; export enum CellKind { Markdown = 1, @@ -582,6 +583,11 @@ export interface INotebookEditorModel extends IEditorModel { revert(options?: IRevertOptions | undefined): Promise; } +export interface INotebookDiffEditorModel extends IEditorModel { + original: INotebookEditorModel; + modified: INotebookEditorModel; +} + export interface INotebookTextModelBackup { metadata: NotebookDocumentMetadata; languages: string[]; @@ -671,3 +677,19 @@ export interface INotebookKernelProvider { resolveKernel(editorId: string, uri: UriComponents, kernelId: string, token: CancellationToken): Promise; executeNotebook(uri: URI, kernelId: string, handle: number | undefined): Promise; } + + +export class CellSequence implements ISequence { + + constructor(readonly textModel: NotebookTextModel) { + } + + getElements(): string[] | number[] | Int32Array { + const hashValue = new Int32Array(this.textModel.cells.length); + for (let i = 0; i < this.textModel.cells.length; i++) { + hashValue[i] = this.textModel.cells[i].getHashValue(); + } + + return hashValue; + } +} From ce182fb07a3bf247c64ee1dbf3ef829beba00d8e Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 28 Jul 2020 17:25:13 -0700 Subject: [PATCH 023/736] improve the whitespace insertion/query speed. --- src/vs/base/browser/ui/list/listView.ts | 10 ++ src/vs/base/browser/ui/list/rangeMap.ts | 107 +++++++++++++++- .../test/browser/ui/list/rangeMap.test.ts | 120 ++++++++++++++++++ .../notebook/browser/notebookBrowser.ts | 2 + .../notebook/browser/notebookDiffEditor.ts | 65 +++++++--- .../browser/notebookDiffEditorInput.ts | 2 - .../notebook/browser/notebookEditorWidget.ts | 10 +- .../notebook/browser/view/notebookCellList.ts | 15 +-- .../common/model/notebookCellTextModel.ts | 3 +- .../notebook/test/testNotebookEditor.ts | 3 +- 10 files changed, 305 insertions(+), 32 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 5cecaeb44e4..e85a2c5e64a 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -375,6 +375,16 @@ export class ListView implements ISpliceable, IDisposable { this.scrollableElement.triggerScrollFromMouseWheelEvent(browserEvent); } + insertWhitespace(index: number, height: number) { + this.rangeMap.insertWhitespace(index, height); + this.rerender(); + } + + updateWhitespace(index: number, newHeight: number) { + this.rangeMap.updateWhitespace(index, newHeight); + this.rerender(); + } + updateElementHeight(index: number, size: number, anchorIndex: number | null): void { if (index < 0 || index >= this.items.length) { return; diff --git a/src/vs/base/browser/ui/list/rangeMap.ts b/src/vs/base/browser/ui/list/rangeMap.ts index 8a732eb4460..2bf805420b2 100644 --- a/src/vs/base/browser/ui/list/rangeMap.ts +++ b/src/vs/base/browser/ui/list/rangeMap.ts @@ -87,9 +87,19 @@ function concat(...groups: IRangedGroup[][]): IRangedGroup[] { return consolidate(groups.reduce((r, g) => r.concat(g), [])); } +export class ListWhitespace { + + constructor( + readonly afterIndex: number, + public height: number, + public prefixSum: number + ) { } +} + export class RangeMap { private groups: IRangedGroup[] = []; + private whitespaces: ListWhitespace[] = []; private _size = 0; splice(index: number, deleteCount: number, items: IItem[] = []): void { @@ -107,6 +117,49 @@ export class RangeMap { this._size = this.groups.reduce((t, g) => t + (g.size * (g.range.end - g.range.start)), 0); } + public static findInsertionIndex(arr: ListWhitespace[], afterIndex: number): number { + let low = 0; + let high = arr.length; + + while (low < high) { + const mid = ((low + high) >>> 1); + + if (afterIndex === arr[mid].afterIndex) { + low = mid; + break; + } else if (afterIndex < arr[mid].afterIndex) { + high = mid; + } else { + low = mid + 1; + } + } + + return low; + } + + insertWhitespace(afterIndex: number, height: number) { + // TODO + // 2. delay prefix sum update + const insertIndex = RangeMap.findInsertionIndex(this.whitespaces, afterIndex); + const prefixSum = insertIndex > 0 ? this.whitespaces[insertIndex - 1].prefixSum + height : height; + const insertedItem = new ListWhitespace(afterIndex, height, prefixSum); + this.whitespaces.splice(insertIndex, 0, insertedItem); + } + + // todo, allow multiple whitespaces after one index + updateWhitespace(afterIndex: number, newHeight: number) { + let delta = 0; + for (let i = 0; i < this.whitespaces.length; i++) { + if (this.whitespaces[i].afterIndex === afterIndex) { + delta = newHeight - this.whitespaces[i].height; + this.whitespaces[i].height = newHeight; + this.whitespaces[i].prefixSum += delta; + } else if (this.whitespaces[i].afterIndex > afterIndex) { + this.whitespaces[i].prefixSum += delta; + } + } + } + /** * Returns the number of items in the range map. */ @@ -124,7 +177,42 @@ export class RangeMap { * Returns the sum of the sizes of all items in the range map. */ get size(): number { - return this._size; + return this._size + + (this.whitespaces.length ? this.whitespaces[this.whitespaces.length - 1]?.prefixSum : 0); + } + + private _getWhitespaceAccumulatedHeightBeforeIndex(index: number): number { + const lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeIndex(index); + + if (lastWhitespaceBeforeLineNumber === -1) { + return 0; + } + + return this.whitespaces[lastWhitespaceBeforeLineNumber].prefixSum; + } + + private _findLastWhitespaceBeforeIndex(index: number): number { + const arr = this.whitespaces; + let low = 0; + let high = arr.length - 1; + + while (low <= high) { + const delta = (high - low) | 0; + const halfDelta = (delta / 2) | 0; + const mid = (low + halfDelta) | 0; + + if (arr[mid].afterIndex < index) { + if (mid + 1 >= arr.length || arr[mid + 1].afterIndex >= index) { + return mid; + } else { + low = (mid + 1) | 0; + } + } else { + high = (mid - 1) | 0; + } + } + + return -1; } /** @@ -142,7 +230,20 @@ export class RangeMap { const count = group.range.end - group.range.start; const newSize = size + (count * group.size); - if (position < newSize) { + if (position < newSize + this._getWhitespaceAccumulatedHeightBeforeIndex(group.range.end + 1)) { + // try to find the right index + let currSize = size; + // position > currSize + all whitespaces before current range + for (let j = group.range.start; j < group.range.end; j++) { + currSize = currSize + group.size; + + if (position >= currSize + this._getWhitespaceAccumulatedHeightBeforeIndex(j + 1)) { + continue; + } else { + return j; + } + } + return index + Math.floor((position - size) / group.size); } @@ -177,7 +278,7 @@ export class RangeMap { const newCount = count + groupCount; if (index < newCount) { - return position + ((index - count) * group.size); + return position + ((index - count) * group.size) + this._getWhitespaceAccumulatedHeightBeforeIndex(index); } position += groupCount * group.size; diff --git a/src/vs/base/test/browser/ui/list/rangeMap.test.ts b/src/vs/base/test/browser/ui/list/rangeMap.test.ts index c92612c1454..491c0dd3c9c 100644 --- a/src/vs/base/test/browser/ui/list/rangeMap.test.ts +++ b/src/vs/base/test/browser/ui/list/rangeMap.test.ts @@ -341,5 +341,125 @@ suite('RangeMap', () => { assert.equal(rangeMap.positionAt(3), 30); assert.equal(rangeMap.positionAt(4), -1); }); + + test('insert whitespace 1', () => { + rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); + assert.equal(rangeMap.indexAt(0), 0); + assert.equal(rangeMap.indexAt(1), 1); + assert.equal(rangeMap.indexAt(5), 5); + assert.equal(rangeMap.indexAt(9), 9); + assert.equal(rangeMap.indexAt(10), 10); + assert.equal(rangeMap.indexAt(11), 10); + + for (let i = 0; i < 10; i++) { + rangeMap.insertWhitespace(i, 1); + } + + assert.equal(rangeMap.indexAt(0), 0); + assert.equal(rangeMap.indexAt(1), 0); + assert.equal(rangeMap.indexAt(2), 1); + assert.equal(rangeMap.indexAt(3), 1); + assert.equal(rangeMap.indexAt(4), 2); + assert.equal(rangeMap.indexAt(5), 2); + assert.equal(rangeMap.indexAt(6), 3); + assert.equal(rangeMap.indexAt(7), 3); + assert.equal(rangeMap.indexAt(8), 4); + assert.equal(rangeMap.indexAt(9), 4); + }); + + test('insert whitespace 2', () => { + rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); + assert.equal(rangeMap.indexAt(0), 0); + assert.equal(rangeMap.indexAt(1), 1); + assert.equal(rangeMap.indexAt(5), 5); + assert.equal(rangeMap.indexAt(9), 9); + assert.equal(rangeMap.indexAt(10), 10); + assert.equal(rangeMap.indexAt(11), 10); + + rangeMap.insertWhitespace(0, 2); + assert.equal(rangeMap.indexAt(0), 0); + assert.equal(rangeMap.indexAt(1), 0); + assert.equal(rangeMap.indexAt(2), 0); + assert.equal(rangeMap.indexAt(3), 1); + assert.equal(rangeMap.indexAt(4), 2); + assert.equal(rangeMap.indexAt(5), 3); + assert.equal(rangeMap.indexAt(10), 8); + + rangeMap.insertWhitespace(3, 3); + assert.equal(rangeMap.indexAt(0), 0); + assert.equal(rangeMap.indexAt(1), 0); + assert.equal(rangeMap.indexAt(2), 0); + assert.equal(rangeMap.indexAt(3), 1); + assert.equal(rangeMap.indexAt(4), 2); + assert.equal(rangeMap.indexAt(5), 3); + assert.equal(rangeMap.indexAt(6), 3); + assert.equal(rangeMap.indexAt(7), 3); + assert.equal(rangeMap.indexAt(8), 3); + assert.equal(rangeMap.indexAt(9), 4); + assert.equal(rangeMap.indexAt(10), 5); + }); + + test('insert whitespace 3', () => { + rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); + assert.equal(rangeMap.indexAt(0), 0); + assert.equal(rangeMap.indexAt(1), 1); + assert.equal(rangeMap.indexAt(5), 5); + assert.equal(rangeMap.indexAt(9), 9); + assert.equal(rangeMap.indexAt(10), 10); + assert.equal(rangeMap.indexAt(11), 10); + + rangeMap.insertWhitespace(0, 2); + assert.equal(rangeMap.indexAt(0), 0); + assert.equal(rangeMap.indexAt(1), 0); + assert.equal(rangeMap.indexAt(2), 0); + assert.equal(rangeMap.indexAt(3), 1); + assert.equal(rangeMap.indexAt(4), 2); + assert.equal(rangeMap.indexAt(5), 3); + assert.equal(rangeMap.indexAt(10), 8); + + rangeMap.insertWhitespace(3, 3); + rangeMap.updateWhitespace(0, 3); + assert.equal(rangeMap.indexAt(0), 0); + assert.equal(rangeMap.indexAt(1), 0); + assert.equal(rangeMap.indexAt(2), 0); + assert.equal(rangeMap.indexAt(3), 0); + assert.equal(rangeMap.indexAt(4), 1); + assert.equal(rangeMap.indexAt(5), 2); + assert.equal(rangeMap.indexAt(6), 3); + assert.equal(rangeMap.indexAt(7), 3); + assert.equal(rangeMap.indexAt(8), 3); + assert.equal(rangeMap.indexAt(9), 3); + assert.equal(rangeMap.indexAt(10), 4); + }); + + test('insert whitespace, positionAt', () => { + rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); + assert.equal(rangeMap.positionAt(0), 0); + assert.equal(rangeMap.positionAt(1), 1); + assert.equal(rangeMap.positionAt(5), 5); + assert.equal(rangeMap.positionAt(9), 9); + + rangeMap.insertWhitespace(0, 2); + assert.equal(rangeMap.positionAt(0), 0); + assert.equal(rangeMap.positionAt(1), 3); + assert.equal(rangeMap.positionAt(2), 4); + assert.equal(rangeMap.positionAt(3), 5); + }); + + test('insert whitespace, positionAt 2', () => { + rangeMap.splice(0, 0, [one]); + rangeMap.splice(1, 0, [two]); + rangeMap.splice(2, 0, [three]); + assert.equal(rangeMap.positionAt(0), 0); + assert.equal(rangeMap.positionAt(1), 1); + assert.equal(rangeMap.positionAt(2), 3); + + rangeMap.insertWhitespace(0, 2); + assert.equal(rangeMap.positionAt(0), 0); + assert.equal(rangeMap.positionAt(1), 3); + assert.equal(rangeMap.positionAt(2), 5); + }); + + }); }); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 2f10e1a1a71..e5438698008 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -459,6 +459,8 @@ export interface INotebookCellList { getFocus(): number[]; setFocus(indexes: number[]): void; setSelection(indexes: number[]): void; + insertWhitespace(index: number, height: number): void; + updateWhitespace(index: number, newHeight: number): void; } export interface BaseCellRenderTemplate { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts index 5ac0f937ecd..7a80cbc3e15 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts @@ -19,6 +19,9 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IDiffResult, LcsDiff } from 'vs/base/common/diff/diff'; import { CellSequence } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookDeltaDecoration } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; +import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; export class NotebookDiffEditor extends BaseEditor { @@ -105,21 +108,45 @@ export class NotebookDiffEditor extends BaseEditor { const originalDecorations: INotebookDeltaDecoration[] = []; const modifiedDecorations: INotebookDeltaDecoration[] = []; - diffResult.changes.forEach(change => { - const original = this._originalWidget?.textModel?.cells.slice(change.originalStart, change.originalStart + change.originalLength) - .map(cell => cell.handle).map(handle => ({ - handle: handle, - options: { className: 'nb-cell-deleted' } - })) || []; - const modified = this._widget?.textModel?.cells.slice(change.modifiedStart, change.modifiedStart + change.modifiedLength) - .map(cell => cell.handle).map(handle => ({ - handle: handle, - options: { className: 'nb-cell-added' } - })) || []; + let viewLayoutUpdateDisposables: IDisposable[] = []; + + diffResult.changes.forEach(change => { + const originalCells = this._originalWidget?.viewModel?.viewCells.slice(change.originalStart, change.originalStart + change.originalLength) || []; + const original = originalCells.map(cell => cell.handle).map(handle => ({ + handle: handle, + options: { className: 'nb-cell-deleted' } + })); + + const modifiedCells = this._widget?.viewModel?.viewCells.slice(change.modifiedStart, change.modifiedStart + change.modifiedLength) || []; + const modified = modifiedCells.map(cell => cell.handle).map(handle => ({ + handle: handle, + options: { className: 'nb-cell-added' } + })); originalDecorations.push(...original); modifiedDecorations.push(...modified); + + this._originalWidget?.insertWhitespace(change.originalStart + change.originalLength - 1, 0); + this._widget?.insertWhitespace(change.modifiedStart + change.modifiedLength - 1, 0); + + const update = () => { + DOM.scheduleAtNextAnimationFrame(() => { + const leftTotalHeight = originalCells.map(cell => (cell instanceof CodeCellViewModel) ? cell.layoutInfo.totalHeight : (cell as MarkdownCellViewModel).layoutInfo.totalHeight) + .reduce((p, c) => { return p + c; }, 0); + const rightTotalHeight = modifiedCells.map(cell => (cell instanceof CodeCellViewModel) ? cell.layoutInfo.totalHeight : (cell as MarkdownCellViewModel).layoutInfo.totalHeight) + .reduce((p, c) => { return p + c; }, 0); + const maxHeight = Math.max(leftTotalHeight, rightTotalHeight); + + this._originalWidget?.updateWhitespace(change.originalStart + change.originalLength - 1, maxHeight - leftTotalHeight); + this._widget?.updateWhitespace(change.modifiedStart + change.modifiedLength - 1, maxHeight - rightTotalHeight); + }, 200); + }; + + viewLayoutUpdateDisposables.push(...[ + ...originalCells.map(cell => (cell instanceof CodeCellViewModel) ? cell.onDidChangeLayout(e => update()) : (cell as MarkdownCellViewModel).onDidChangeLayout(e => update())), + ...modifiedCells.map(cell => (cell instanceof CodeCellViewModel) ? cell.onDidChangeLayout(e => update()) : (cell as MarkdownCellViewModel).onDidChangeLayout(e => update())), + ]); }); this._originalCellDecorations = this._originalWidget.deltaCellDecorations(this._originalCellDecorations, originalDecorations); @@ -137,9 +164,10 @@ export class NotebookDiffEditor extends BaseEditor { setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void { super.setEditorVisible(visible, group); if (!visible) { - if (this.input && this._widget) { + if (this.input) { // the widget is not transfered to other editor inputs - this._widget.onWillHide(); + this._widget?.onWillHide(); + this._originalWidget?.onWillHide(); } } @@ -152,9 +180,14 @@ export class NotebookDiffEditor extends BaseEditor { clearInput(): void { - if (this._widget) { - this._widget.onWillHide(); - } + this._widget?.onWillHide(); + this._originalWidget?.onWillHide(); + + this._widget?.dispose(); + this._originalWidget?.dispose(); + + this._widget = null; + this._originalWidget = null; super.clearInput(); } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts index 5a001605257..aefef967792 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts @@ -14,8 +14,6 @@ import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; import { IReference } from 'vs/base/common/lifecycle'; import { INotebookEditorModel, INotebookDiffEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { NotebookEditorModel } from '../common/notebookEditorModel'; -import { IEditorModel } from 'vs/editor/common/editorCommon'; interface NotebookEditorInputOptions { startDirty?: boolean; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 365cb52632e..2e01180f152 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -52,7 +52,7 @@ import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/vie import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { notebookKernelProviderAssociationsSettingId, NotebookKernelProviderAssociations } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation'; import { ScrollEvent } from 'vs/base/common/scrollable'; -import { editorGutterAddedBackground, editorGutterDeletedBackground, editorGutterModifiedBackground } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; +import { editorGutterModifiedBackground } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; const $ = DOM.$; @@ -1427,6 +1427,14 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor //#region MISC + insertWhitespace(index: number, height: number) { + this._list?.insertWhitespace(index, height); + } + + updateWhitespace(index: number, newHeight: number) { + this._list?.updateWhitespace(index, newHeight); + } + deltaCellDecorations(oldDecorations: string[], newDecorations: INotebookDeltaDecoration[]): string[] { return this._notebookViewModel?.deltaCellDecorations(oldDecorations, newDecorations) || []; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index 1a804a736ff..09bef62165c 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -210,18 +210,10 @@ export class NotebookCellList extends WorkbenchList implements ID }, ); - let scrolling = false; - - this._localDisposableStore.add(this.onWillScroll(() => { - scrolling = true; - })); - this._localDisposableStore.add(this.onDidScroll(() => { if (this._cellListGutter.scrollTop !== this.scrollTop) { this._cellListGutter.scrollTop = this.scrollTop; } - - scrolling = false; })); //TODO @@ -641,6 +633,13 @@ export class NotebookCellList extends WorkbenchList implements ID this.view.triggerScrollFromMouseWheelEvent(browserEvent); } + insertWhitespace(index: number, height: number) { + this.view.insertWhitespace(index, height); + } + + updateWhitespace(index: number, newHeight: number) { + this.view.updateWhitespace(index, newHeight); + } updateElementHeight2(element: ICellViewModel, size: number): void { const index = this._getViewIndexUpperBound(element); diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts index c26a4856557..14e34b19ffb 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts @@ -108,7 +108,8 @@ export class NotebookCellTextModel extends Disposable implements ICell { return this._hash; } - this._hash = hash([hash(this.getValue()), this._metadata]); + // this._hash = hash([hash(this.getValue()), this._metadata]); + this._hash = hash(this.getValue()); return this._hash; } diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index 6f8b49a3cf5..60aaf162302 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -12,7 +12,7 @@ import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { Range } from 'vs/editor/common/core/range'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { EditorModel } from 'vs/workbench/common/editor'; -import { ICellRange, ICellViewModel, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo, INotebookDeltaDecoration } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { ICellRange, ICellViewModel, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo, INotebookDeltaDecoration, INotebookEditorCreationOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer'; import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { CellViewModel, IModelDecorationsChangeAccessor, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; @@ -58,6 +58,7 @@ export class TestNotebookEditor implements INotebookEditor { get viewModel() { return undefined; } + creationOptions: INotebookEditorCreationOptions = { isEmbeded: false }; constructor( ) { } From 5feda3b0a60c84bb7335d5548b397ae6e47cf838 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 28 Jul 2020 17:40:22 -0700 Subject: [PATCH 024/736] update prefix sum and index when range map is updated. --- src/vs/base/browser/ui/list/rangeMap.ts | 25 ++++++++++++- .../test/browser/ui/list/rangeMap.test.ts | 37 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/list/rangeMap.ts b/src/vs/base/browser/ui/list/rangeMap.ts index 2bf805420b2..83391bdd146 100644 --- a/src/vs/base/browser/ui/list/rangeMap.ts +++ b/src/vs/base/browser/ui/list/rangeMap.ts @@ -90,7 +90,7 @@ function concat(...groups: IRangedGroup[][]): IRangedGroup[] { export class ListWhitespace { constructor( - readonly afterIndex: number, + public afterIndex: number, public height: number, public prefixSum: number ) { } @@ -115,6 +115,29 @@ export class RangeMap { this.groups = concat(before, middle, after); this._size = this.groups.reduce((t, g) => t + (g.size * (g.range.end - g.range.start)), 0); + + const deleteRange = deleteCount > 0 ? [index, index + deleteCount - 1] : []; + const indexDelta = items.length - deleteCount; + let prefixSumDelta = 0; + const pendingRemovalWhitespace: number[] = []; + for (let i = 0; i < this.whitespaces.length; i++) { + const whitespace = this.whitespaces[i]; + + if (whitespace.afterIndex < index) { + continue; + } else if (deleteRange.length > 0 && whitespace.afterIndex >= deleteRange[0] && whitespace.afterIndex <= deleteRange[1]) { + // should be deleted + pendingRemovalWhitespace.push(i); + prefixSumDelta += whitespace.height; + } else { + whitespace.afterIndex += indexDelta; + whitespace.prefixSum -= prefixSumDelta; + } + } + + pendingRemovalWhitespace.reverse().forEach(index => { + this.whitespaces.splice(index, 1); + }); } public static findInsertionIndex(arr: ListWhitespace[], afterIndex: number): number { diff --git a/src/vs/base/test/browser/ui/list/rangeMap.test.ts b/src/vs/base/test/browser/ui/list/rangeMap.test.ts index 491c0dd3c9c..8805ea8a2cc 100644 --- a/src/vs/base/test/browser/ui/list/rangeMap.test.ts +++ b/src/vs/base/test/browser/ui/list/rangeMap.test.ts @@ -461,5 +461,42 @@ suite('RangeMap', () => { }); + test('update whitespace when range map splices', () => { + rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); + + rangeMap.insertWhitespace(1, 2); + rangeMap.insertWhitespace(5, 3); + assert.equal(rangeMap.positionAt(0), 0); + assert.equal(rangeMap.positionAt(1), 1); + assert.equal(rangeMap.positionAt(2), 4); + assert.equal(rangeMap.positionAt(3), 5); + assert.equal(rangeMap.positionAt(4), 6); + assert.equal(rangeMap.positionAt(5), 7); + assert.equal(rangeMap.positionAt(6), 11); + assert.equal(rangeMap.positionAt(7), 12); + assert.equal(rangeMap.positionAt(8), 13); + + assert.equal(rangeMap.indexAt(0), 0); + assert.equal(rangeMap.indexAt(1), 1); + assert.equal(rangeMap.indexAt(2), 1); + assert.equal(rangeMap.indexAt(3), 1); + assert.equal(rangeMap.indexAt(4), 2); + + rangeMap.splice(1, 2); + assert.equal(rangeMap.positionAt(0), 0); + assert.equal(rangeMap.positionAt(1), 1); + assert.equal(rangeMap.positionAt(2), 2); + assert.equal(rangeMap.positionAt(3), 3); + assert.equal(rangeMap.positionAt(4), 7); + assert.equal(rangeMap.positionAt(5), 8); + assert.equal(rangeMap.positionAt(6), 9); + + assert.equal(rangeMap.indexAt(0), 0); + assert.equal(rangeMap.indexAt(1), 1); + assert.equal(rangeMap.indexAt(2), 2); + assert.equal(rangeMap.indexAt(3), 3); + assert.equal(rangeMap.indexAt(4), 3); + }); + }); }); From 1ea9bbf7d24d8c543992dcc0d3fe3373f1ca7053 Mon Sep 17 00:00:00 2001 From: Alexey Schebelev Date: Wed, 29 Jul 2020 16:57:21 +0300 Subject: [PATCH 025/736] Add *.xhtm file extension `xhtm` - is also valid file extension for XHTML files. It is like `htm` and `html` for HTML. --- extensions/html/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/html/package.json b/extensions/html/package.json index 065eb26669b..3e685df1acf 100644 --- a/extensions/html/package.json +++ b/extensions/html/package.json @@ -20,6 +20,7 @@ ".htm", ".shtml", ".xhtml", + ".xhtm", ".mdoc", ".jsp", ".asp", From f40a29d68041086247cc846db0f7db768983dd98 Mon Sep 17 00:00:00 2001 From: AlexxNB Date: Wed, 29 Jul 2020 18:12:24 +0300 Subject: [PATCH 026/736] xhtm->xht --- extensions/html/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/html/package.json b/extensions/html/package.json index 3e685df1acf..6b1eac2d702 100644 --- a/extensions/html/package.json +++ b/extensions/html/package.json @@ -20,7 +20,7 @@ ".htm", ".shtml", ".xhtml", - ".xhtm", + ".xht", ".mdoc", ".jsp", ".asp", From 9a859dcea4804fab14c00beacaa57ee0f12dea18 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 29 Jul 2020 11:48:19 -0700 Subject: [PATCH 027/736] range map might be deleted when splice --- src/vs/base/browser/ui/list/rangeMap.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/vs/base/browser/ui/list/rangeMap.ts b/src/vs/base/browser/ui/list/rangeMap.ts index 83391bdd146..d6764a778b7 100644 --- a/src/vs/base/browser/ui/list/rangeMap.ts +++ b/src/vs/base/browser/ui/list/rangeMap.ts @@ -92,10 +92,12 @@ export class ListWhitespace { constructor( public afterIndex: number, public height: number, + // height of all whitespaces before this whitespace (inclusive) public prefixSum: number ) { } } +// [ { start: 0, len: 2, size: 2 }, { start: 2, len: 1, size: 3 }, {} ] export class RangeMap { private groups: IRangedGroup[] = []; @@ -167,20 +169,34 @@ export class RangeMap { const prefixSum = insertIndex > 0 ? this.whitespaces[insertIndex - 1].prefixSum + height : height; const insertedItem = new ListWhitespace(afterIndex, height, prefixSum); this.whitespaces.splice(insertIndex, 0, insertedItem); + + for (let i = insertIndex + 1; i < this.whitespaces.length; i++) { + this.whitespaces[i].prefixSum += height; + } } // todo, allow multiple whitespaces after one index updateWhitespace(afterIndex: number, newHeight: number) { let delta = 0; + let findWhitespace = false; for (let i = 0; i < this.whitespaces.length; i++) { if (this.whitespaces[i].afterIndex === afterIndex) { delta = newHeight - this.whitespaces[i].height; this.whitespaces[i].height = newHeight; this.whitespaces[i].prefixSum += delta; + findWhitespace = true; } else if (this.whitespaces[i].afterIndex > afterIndex) { + if (!findWhitespace) { + this.insertWhitespace(afterIndex, newHeight); + return; + } this.whitespaces[i].prefixSum += delta; } } + + if (!findWhitespace) { + this.insertWhitespace(afterIndex, newHeight); + } } /** From dac1344fff3ca4fb893c5c44a8861eabf574def1 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 29 Jul 2020 11:56:39 -0700 Subject: [PATCH 028/736] 200ms delay for diff. --- .../notebook/browser/contrib/scm/scm.ts | 122 ++++++++++-------- .../notebook/browser/notebookDiffEditor.ts | 4 +- 2 files changed, 67 insertions(+), 59 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts b/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts index 4faa80fd842..056d480860f 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts @@ -8,7 +8,7 @@ import { INotebookEditorContribution, INotebookEditor, INotebookDeltaDecoration import { registerNotebookContribution } from '../../notebookEditorExtensions'; import { ISCMService } from 'vs/workbench/contrib/scm/common/scm'; import { createProviderComparer } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; -import { first } from 'vs/base/common/async'; +import { first, ThrottledDelayer } from 'vs/base/common/async'; import { INotebookService } from '../../../common/notebookService'; import { LcsDiff } from 'vs/base/common/diff/diff'; import { CellSequence } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -17,6 +17,7 @@ export class SCMController extends Disposable implements INotebookEditorContribu static id: string = 'workbench.notebook.findController'; private _lastDecorationId: string[] = []; private _localDisposable = new DisposableStore(); + private _diffDelayer = new ThrottledDelayer(200); private _lastVersion = -1; @@ -32,6 +33,7 @@ export class SCMController extends Disposable implements INotebookEditorContribu if (!this._notebookEditor.creationOptions.isEmbeded) { this._register(this._notebookEditor.onDidChangeModel(() => { this._localDisposable.clear(); + this._diffDelayer.cancel(); this.update(); if (this._notebookEditor.textModel) { @@ -50,69 +52,75 @@ export class SCMController extends Disposable implements INotebookEditorContribu } async update() { - - const modifiedDocument = this._notebookEditor.textModel; - if (!modifiedDocument) { + if (!this._diffDelayer) { return; } - if (this._lastVersion >= modifiedDocument.versionId) { - return; - } - - this._lastVersion = modifiedDocument.versionId; - - const uri = modifiedDocument.uri; - const providers = this._scmService.repositories.map(r => r.provider); - const rootedProviders = providers.filter(p => !!p.rootUri); - - rootedProviders.sort(createProviderComparer(uri)); - - const result = await first(rootedProviders.map(p => () => p.getOriginalResource(uri))); - - if (!result) { - this._clear(); - return; - } - - const originalDocument = await this._notebookService.resolveNotebook(modifiedDocument.viewType, result, false); - - if (!originalDocument) { - this._clear(); - return; - } - - const diff = new LcsDiff(new CellSequence(originalDocument), new CellSequence(modifiedDocument)); - const diffResult = diff.ComputeDiff(false); - - const decorations: INotebookDeltaDecoration[] = []; - diffResult.changes.forEach(change => { - if (change.originalLength === 0) { - // doesn't exist in original - for (let i = 0; i < change.modifiedLength; i++) { - decorations.push({ - handle: modifiedDocument.cells[change.modifiedStart + i].handle, - options: { gutterClassName: 'nb-gutter-cell-inserted' } - }); + await this._diffDelayer + .trigger(async () => { + const modifiedDocument = this._notebookEditor.textModel; + if (!modifiedDocument) { + return; } - } else { - if (change.modifiedLength === 0) { - // diff.deleteCount - // removed from original - } else { - // modification - for (let i = 0; i < change.modifiedLength; i++) { - decorations.push({ - handle: modifiedDocument.cells[change.modifiedStart + i].handle, - options: { gutterClassName: 'nb-gutter-cell-changed' } - }); + + if (this._lastVersion >= modifiedDocument.versionId) { + return; + } + + this._lastVersion = modifiedDocument.versionId; + + const uri = modifiedDocument.uri; + const providers = this._scmService.repositories.map(r => r.provider); + const rootedProviders = providers.filter(p => !!p.rootUri); + + rootedProviders.sort(createProviderComparer(uri)); + + const result = await first(rootedProviders.map(p => () => p.getOriginalResource(uri))); + + if (!result) { + this._clear(); + return; + } + + const originalDocument = await this._notebookService.resolveNotebook(modifiedDocument.viewType, result, false); + + if (!originalDocument) { + this._clear(); + return; + } + + const diff = new LcsDiff(new CellSequence(originalDocument), new CellSequence(modifiedDocument)); + const diffResult = diff.ComputeDiff(false); + + const decorations: INotebookDeltaDecoration[] = []; + diffResult.changes.forEach(change => { + if (change.originalLength === 0) { + // doesn't exist in original + for (let i = 0; i < change.modifiedLength; i++) { + decorations.push({ + handle: modifiedDocument.cells[change.modifiedStart + i].handle, + options: { gutterClassName: 'nb-gutter-cell-inserted' } + }); + } + } else { + if (change.modifiedLength === 0) { + // diff.deleteCount + // removed from original + } else { + // modification + for (let i = 0; i < change.modifiedLength; i++) { + decorations.push({ + handle: modifiedDocument.cells[change.modifiedStart + i].handle, + options: { gutterClassName: 'nb-gutter-cell-changed' } + }); + } + } } - } - } - }); + }); - this._lastDecorationId = this._notebookEditor.deltaCellDecorations(this._lastDecorationId, decorations); + this._lastDecorationId = this._notebookEditor.deltaCellDecorations(this._lastDecorationId, decorations); + }); } private _clear() { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts index 7a80cbc3e15..6127a357c8e 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts @@ -131,7 +131,7 @@ export class NotebookDiffEditor extends BaseEditor { this._widget?.insertWhitespace(change.modifiedStart + change.modifiedLength - 1, 0); const update = () => { - DOM.scheduleAtNextAnimationFrame(() => { + this._register(DOM.scheduleAtNextAnimationFrame(() => { const leftTotalHeight = originalCells.map(cell => (cell instanceof CodeCellViewModel) ? cell.layoutInfo.totalHeight : (cell as MarkdownCellViewModel).layoutInfo.totalHeight) .reduce((p, c) => { return p + c; }, 0); const rightTotalHeight = modifiedCells.map(cell => (cell instanceof CodeCellViewModel) ? cell.layoutInfo.totalHeight : (cell as MarkdownCellViewModel).layoutInfo.totalHeight) @@ -140,7 +140,7 @@ export class NotebookDiffEditor extends BaseEditor { this._originalWidget?.updateWhitespace(change.originalStart + change.originalLength - 1, maxHeight - leftTotalHeight); this._widget?.updateWhitespace(change.modifiedStart + change.modifiedLength - 1, maxHeight - rightTotalHeight); - }, 200); + }, 200)); }; viewLayoutUpdateDisposables.push(...[ From dd4fde9ac5096ef4b0a61d51a1114e8a79e19956 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 29 Jul 2020 15:12:02 -0700 Subject: [PATCH 029/736] diff delayer. --- .../notebook/browser/contrib/scm/scm.ts | 60 +++++++++++++------ .../notebook/browser/notebookDiffEditor.ts | 24 +++++++- .../browser/notebookDiffEditorInput.ts | 11 +++- .../contrib/notebook/common/notebookCommon.ts | 1 + 4 files changed, 75 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts b/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts index 056d480860f..01e4490b375 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts @@ -12,11 +12,17 @@ import { first, ThrottledDelayer } from 'vs/base/common/async'; import { INotebookService } from '../../../common/notebookService'; import { LcsDiff } from 'vs/base/common/diff/diff'; import { CellSequence } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; +import { FileService } from 'vs/platform/files/common/fileService'; +import { IFileService } from 'vs/platform/files/common/files'; +import { URI } from 'vs/base/common/uri'; export class SCMController extends Disposable implements INotebookEditorContribution { static id: string = 'workbench.notebook.findController'; private _lastDecorationId: string[] = []; private _localDisposable = new DisposableStore(); + private _originalDocument: NotebookTextModel | undefined = undefined; + private _originalResourceDisposableStore = new DisposableStore(); private _diffDelayer = new ThrottledDelayer(200); private _lastVersion = -1; @@ -24,6 +30,7 @@ export class SCMController extends Disposable implements INotebookEditorContribu constructor( private readonly _notebookEditor: INotebookEditor, + @IFileService private readonly _fileService: FileService, @ISCMService private readonly _scmService: ISCMService, @INotebookService private readonly _notebookService: INotebookService @@ -51,6 +58,39 @@ export class SCMController extends Disposable implements INotebookEditorContribu } } + private async _resolveNotebookDocument(uri: URI, viewType: string) { + const providers = this._scmService.repositories.map(r => r.provider); + const rootedProviders = providers.filter(p => !!p.rootUri); + + rootedProviders.sort(createProviderComparer(uri)); + + const result = await first(rootedProviders.map(p => () => p.getOriginalResource(uri))); + + if (!result) { + this._originalDocument = undefined; + this._originalResourceDisposableStore.clear(); + return; + } + + if (result.toString() === this._originalDocument?.uri.toString()) { + // original document not changed + return; + } + + this._originalResourceDisposableStore.add(this._fileService.watch(result)); + this._originalResourceDisposableStore.add(this._fileService.onDidFilesChange(e => { + if (e.changes.find(change => change.resource.toString() === result.toString())) { + this._originalDocument = undefined; + this._originalResourceDisposableStore.clear(); + this.update(); + } + })); + + const originalDocument = await this._notebookService.resolveNotebook(viewType, result, false); + + this._originalDocument = originalDocument; + } + async update() { if (!this._diffDelayer) { return; @@ -68,28 +108,14 @@ export class SCMController extends Disposable implements INotebookEditorContribu } this._lastVersion = modifiedDocument.versionId; + await this._resolveNotebookDocument(modifiedDocument.uri, modifiedDocument.viewType); - const uri = modifiedDocument.uri; - const providers = this._scmService.repositories.map(r => r.provider); - const rootedProviders = providers.filter(p => !!p.rootUri); - - rootedProviders.sort(createProviderComparer(uri)); - - const result = await first(rootedProviders.map(p => () => p.getOriginalResource(uri))); - - if (!result) { + if (!this._originalDocument) { this._clear(); return; } - const originalDocument = await this._notebookService.resolveNotebook(modifiedDocument.viewType, result, false); - - if (!originalDocument) { - this._clear(); - return; - } - - const diff = new LcsDiff(new CellSequence(originalDocument), new CellSequence(modifiedDocument)); + const diff = new LcsDiff(new CellSequence(this._originalDocument), new CellSequence(modifiedDocument)); const diffResult = diff.ComputeDiff(false); const decorations: INotebookDeltaDecoration[] = []; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts index 6127a357c8e..f835bb8eaa8 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts @@ -17,11 +17,13 @@ import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsSe import { NotebookDiffEditorInput } from './notebookDiffEditorInput'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IDiffResult, LcsDiff } from 'vs/base/common/diff/diff'; -import { CellSequence } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellSequence, INotebookDiffEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookDeltaDecoration } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { IDisposable } from 'vs/base/common/lifecycle'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; +import { FileService } from 'vs/platform/files/common/fileService'; +import { IFileService } from 'vs/platform/files/common/files'; export class NotebookDiffEditor extends BaseEditor { @@ -38,6 +40,7 @@ export class NotebookDiffEditor extends BaseEditor { constructor( + @IFileService private readonly fileService: FileService, @ITelemetryService telemetryService: ITelemetryService, @IThemeService themeService: IThemeService, @IInstantiationService private readonly instantiationService: IInstantiationService, @@ -95,6 +98,25 @@ export class NotebookDiffEditor extends BaseEditor { } })); + this._register(this.fileService.watch(model.original.resource)); + this._register(this.fileService.onDidFilesChange(async e => { + if (e.changes.find(change => change.resource.toString() === model.original.resource.toString())) { + await model.resolveOriginalFromDisk(); + this._update(model); + } + })); + + this._register(model.modified.notebook.onDidChangeContent(() => { + this._update(model); + })); + + this._register(model.modified.notebook.onDidChangeCells(() => { + this._update(model); + })); + this._update(model); + } + + private _update(model: INotebookDiffEditorModel) { const diff = new LcsDiff(new CellSequence(model.original.notebook), new CellSequence(model.modified.notebook)); const diffResult = diff.ComputeDiff(false); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts index aefef967792..efb4c9a703f 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts @@ -14,6 +14,7 @@ import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; import { IReference } from 'vs/base/common/lifecycle'; import { INotebookEditorModel, INotebookDiffEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookEditorModel'; interface NotebookEditorInputOptions { startDirty?: boolean; @@ -21,8 +22,8 @@ interface NotebookEditorInputOptions { class NotebookDiffEditorModel extends EditorModel implements INotebookDiffEditorModel { constructor( - readonly original: INotebookEditorModel, - readonly modified: INotebookEditorModel, + readonly original: NotebookEditorModel, + readonly modified: NotebookEditorModel, ) { super(); } @@ -34,6 +35,10 @@ class NotebookDiffEditorModel extends EditorModel implements INotebookDiffEditor return this; } + async resolveOriginalFromDisk() { + await this.original.load({ forceReadFromDisk: true }); + } + dispose(): void { } @@ -203,7 +208,7 @@ ${patterns} // } } - return new NotebookDiffEditorModel(this._originalTextModel!.object, this._textModel.object); + return new NotebookDiffEditorModel(this._originalTextModel!.object as NotebookEditorModel, this._textModel.object as NotebookEditorModel); } matches(otherInput: unknown): boolean { diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 87eaa7da738..43697322780 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -586,6 +586,7 @@ export interface INotebookEditorModel extends IEditorModel { export interface INotebookDiffEditorModel extends IEditorModel { original: INotebookEditorModel; modified: INotebookEditorModel; + resolveOriginalFromDisk(): Promise; } export interface INotebookTextModelBackup { From d9b4226acc64481123a00d3e005d903921544755 Mon Sep 17 00:00:00 2001 From: Aman Gupta Date: Fri, 31 Jul 2020 11:53:44 +0530 Subject: [PATCH 030/736] empty-line-wrap-fix --- extensions/emmet/src/abbreviationActions.ts | 31 +++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index 367f4de2e9d..aa1d2563dbf 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -606,6 +606,25 @@ function expandAbbreviationInRange(editor: vscode.TextEditor, expandAbbrList: Ex return Promise.resolve(false); } +/* +* Walks the tree rooted at root and apply function fn on each node. +* if fn return false at any node, the further processing of tree is stopped. +*/ +function walk(root: any, fn: ((node: any) => boolean)): boolean { + let ctx = root; + while (ctx) { + + let next = ctx.next; + if (fn(ctx) === false || walk(ctx.firstChild, fn) === false) { + return false; + } + + ctx = next; + } + + return true; +} + /** * Expands abbreviation as detailed in given input. */ @@ -648,6 +667,18 @@ function expandAbbr(input: ExpandAbbreviationInput): string | undefined { wrappingNode.value = '\n\t' + wrappingNode.value + '\n'; } } + + // Below fixes https://github.com/microsoft/vscode/issues/78219 + // walk the tree and remove tags for empty values + walk(parsedAbbr, node => { + if (node.name !== null && node.value === '' && !node.isSelfClosing && node.children.length === 0) { + node.name = ''; + node.value = '\n'; + } + + return true; + }); + expandedText = helper.expandAbbreviation(parsedAbbr, expandOptions); // All $anyword would have been escaped by the emmet helper. // Remove the escaping backslash from $TM_SELECTED_TEXT so that VS Code Snippet controller can treat it as a variable From 259f4d28da141fa72dfd5f2b4abb0c1f30277a53 Mon Sep 17 00:00:00 2001 From: Ikko Ashimine Date: Tue, 4 Aug 2020 01:10:05 +0900 Subject: [PATCH 031/736] Fix typo 'enviroment' to 'environment' * comment --- src/typings/node.processEnv-ext.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/typings/node.processEnv-ext.d.ts b/src/typings/node.processEnv-ext.d.ts index fec557ff2a7..4ca44ec5b37 100644 --- a/src/typings/node.processEnv-ext.d.ts +++ b/src/typings/node.processEnv-ext.d.ts @@ -8,7 +8,7 @@ declare namespace NodeJS { export interface Process { /** - * The lazy enviroment is a promise that resolves to `process.env` + * The lazy environment is a promise that resolves to `process.env` * once the process is resolved. The use-case is VS Code running * on Linux/macOS when being launched via a launcher. Then the env * (as defined in .bashrc etc) isn't properly set and needs to be From 59ef2e37e79a85f2eb1382eaad5dc842fa611f21 Mon Sep 17 00:00:00 2001 From: annkamsk Date: Mon, 3 Aug 2020 19:19:53 +0200 Subject: [PATCH 032/736] Replace `innerHTML` with `innerText` when assigning non-html string --- src/vs/base/browser/ui/iconLabel/iconLabel.ts | 8 ++++---- src/vs/base/browser/ui/inputbox/inputBox.ts | 4 ++-- src/vs/base/browser/ui/tree/abstractTree.ts | 2 +- src/vs/base/parts/quickinput/browser/quickInput.ts | 2 +- .../code/electron-sandbox/issue/issueReporterMain.ts | 4 ++-- .../processExplorer/processExplorerMain.ts | 2 +- .../contrib/parameterHints/parameterHintsWidget.ts | 4 ++-- src/vs/editor/contrib/suggest/suggestWidget.ts | 2 +- src/vs/workbench/browser/actions/developerActions.ts | 2 +- src/vs/workbench/browser/parts/editor/editorPart.ts | 2 +- .../workbench/browser/parts/views/viewPaneContainer.ts | 6 +++--- .../contrib/comments/browser/commentsTreeViewer.ts | 2 +- .../contrib/extensions/browser/extensionEditor.ts | 4 ++-- .../contrib/extensions/browser/extensionsWidgets.ts | 4 ++-- .../notebook/browser/view/renderers/cellRenderer.ts | 6 +++--- .../notebook/browser/view/renderers/markdownCell.ts | 8 ++++---- .../notebook/browser/view/renderers/webviewPreloads.ts | 2 +- .../contrib/preferences/browser/settingsTree.ts | 10 +++++----- src/vs/workbench/test/browser/part.test.ts | 6 +++--- 19 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index 93c4230477b..de211c787a3 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -187,14 +187,14 @@ class Label { if (typeof label === 'string') { if (!this.singleLabel) { - this.container.innerHTML = ''; + this.container.innerText = ''; dom.removeClass(this.container, 'multiple'); this.singleLabel = dom.append(this.container, dom.$('a.label-name', { id: options?.domId })); } this.singleLabel.textContent = label; } else { - this.container.innerHTML = ''; + this.container.innerText = ''; dom.addClass(this.container, 'multiple'); this.singleLabel = undefined; @@ -250,7 +250,7 @@ class LabelWithHighlights { if (typeof label === 'string') { if (!this.singleLabel) { - this.container.innerHTML = ''; + this.container.innerText = ''; dom.removeClass(this.container, 'multiple'); this.singleLabel = new HighlightedLabel(dom.append(this.container, dom.$('a.label-name', { id: options?.domId })), this.supportCodicons); } @@ -258,7 +258,7 @@ class LabelWithHighlights { this.singleLabel.set(label, options?.matches, options?.title, options?.labelEscapeNewLines); } else { - this.container.innerHTML = ''; + this.container.innerText = ''; dom.addClass(this.container, 'multiple'); this.singleLabel = undefined; diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index a910e375661..2410c5234bb 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -170,7 +170,7 @@ export class InputBox extends Widget { this.maxHeight = typeof this.options.flexibleMaxHeight === 'number' ? this.options.flexibleMaxHeight : Number.POSITIVE_INFINITY; this.mirror = dom.append(wrapper, $('div.mirror')); - this.mirror.innerHTML = ' '; + this.mirror.innerText = '\u00a0'; this.scrollableElement = new ScrollableElement(this.element, { vertical: ScrollbarVisibility.Auto }); @@ -529,7 +529,7 @@ export class InputBox extends Widget { if (mirrorTextContent) { this.mirror.textContent = value + suffix; } else { - this.mirror.innerHTML = ' '; + this.mirror.innerText = '\u00a0'; } this.layout(); diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index be82f6156e2..16e17fcf763 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -881,7 +881,7 @@ class TypeFilterController implements IDisposable { this.messageDomNode.textContent = localize('empty', "No elements found"); this._empty = true; } else { - this.messageDomNode.innerHTML = ''; + this.messageDomNode.innerText = ''; this._empty = false; } diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index 25475e8e20d..81019137540 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -278,7 +278,7 @@ class QuickInput extends Disposable implements IQuickInput { if (title && this.ui.title.textContent !== title) { this.ui.title.textContent = title; } else if (!title && this.ui.title.innerHTML !== ' ') { - this.ui.title.innerHTML = ' '; + this.ui.title.innerText = '\u00a0;'; } const description = this.getDescription(); if (this.ui.description.textContent !== description) { diff --git a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts index 9e7634b2791..c7c038286e0 100644 --- a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts @@ -553,7 +553,7 @@ export class IssueReporter extends Disposable { private clearSearchResults(): void { const similarIssues = this.getElementById('similar-issues')!; - similarIssues.innerHTML = ''; + similarIssues.innerText = ''; this.numberOfSearchResultsDisplayed = 0; } @@ -564,7 +564,7 @@ export class IssueReporter extends Disposable { window.fetch(`https://api.github.com/search/issues?q=${query}`).then((response) => { response.json().then(result => { - similarIssues.innerHTML = ''; + similarIssues.innerText = ''; if (result && result.items) { this.displaySearchResults(result.items); } else { diff --git a/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts b/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts index 3c88a619a55..b921e461ca9 100644 --- a/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts +++ b/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts @@ -267,7 +267,7 @@ class ProcessExplorer { return; } - container.innerHTML = ''; + container.innerText = ''; this.listeners.clear(); const tableHead = document.createElement('thead'); diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index e140bdc4cd5..c34c98c2f97 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -194,8 +194,8 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { dom.toggleClass(this.domNodes.element, 'multiple', multiple); this.keyMultipleSignatures.set(multiple); - this.domNodes.signature.innerHTML = ''; - this.domNodes.docs.innerHTML = ''; + this.domNodes.signature.innerText = ''; + this.domNodes.docs.innerText = ''; const signature = hints.signatures[hints.activeSignature]; if (!signature) { diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 7b19fefb903..107bee50e37 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -373,7 +373,7 @@ class SuggestionDetails { this.docs.textContent = documentation; } else { this.docs.classList.add('markdown-docs'); - this.docs.innerHTML = ''; + this.docs.innerText = ''; const renderedContents = this.markdownRenderer.render(documentation); this.renderDisposeable = renderedContents; this.docs.appendChild(renderedContents.element); diff --git a/src/vs/workbench/browser/actions/developerActions.ts b/src/vs/workbench/browser/actions/developerActions.ts index 60c53ae74b4..d769e61957c 100644 --- a/src/vs/workbench/browser/actions/developerActions.ts +++ b/src/vs/workbench/browser/actions/developerActions.ts @@ -179,7 +179,7 @@ class ToggleScreencastModeAction extends Action2 { || length > 20 || event.keyCode === KeyCode.Backspace || event.keyCode === KeyCode.Escape ) { - keyboardMarker.innerHTML = ''; + keyboardMarker.innerText = ''; length = 0; } diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index ca5e8da0ea5..55ad66233a1 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -58,7 +58,7 @@ class GridWidgetView implements IView { } set gridWidget(grid: Grid | undefined) { - this.element.innerHTML = ''; + this.element.innerText = ''; if (grid) { this.element.appendChild(grid.element); diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index 8c21efbf18b..9180af49c0f 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -506,7 +506,7 @@ export abstract class ViewPane extends Pane implements IView { if (!this.shouldShowWelcome()) { removeClass(this.bodyContainer, 'welcome'); - this.viewWelcomeContainer.innerHTML = ''; + this.viewWelcomeContainer.innerText = ''; this.scrollableElement.scanDomNode(); return; } @@ -515,14 +515,14 @@ export abstract class ViewPane extends Pane implements IView { if (contents.length === 0) { removeClass(this.bodyContainer, 'welcome'); - this.viewWelcomeContainer.innerHTML = ''; + this.viewWelcomeContainer.innerText = ''; this.scrollableElement.scanDomNode(); return; } const disposables = new DisposableStore(); addClass(this.bodyContainer, 'welcome'); - this.viewWelcomeContainer.innerHTML = ''; + this.viewWelcomeContainer.innerText = ''; let buttonIndex = 0; diff --git a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts index 0abbf8b7626..8a2f4de0c2b 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts @@ -120,7 +120,7 @@ export class CommentNodeRenderer implements IListRenderer renderElement(node: ITreeNode, index: number, templateData: ICommentThreadTemplateData, height: number | undefined): void { templateData.userName.textContent = node.element.comment.userName; - templateData.commentText.innerHTML = ''; + templateData.commentText.innerText = ''; const disposables = new DisposableStore(); templateData.disposables.push(disposables); const renderedComment = renderMarkdown(node.element.comment.body, { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 51ca34594bc..347f08e869c 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -429,7 +429,7 @@ export class ExtensionEditor extends BaseEditor { } this.setSubText(extension, reloadAction, template); - template.content.innerHTML = ''; // Clear content before setting navbar actions. + template.content.innerText = ''; // Clear content before setting navbar actions. template.navbar.clear(); @@ -560,7 +560,7 @@ export class ExtensionEditor extends BaseEditor { } this.contentDisposables.clear(); - template.content.innerHTML = ''; + template.content.innerText = ''; this.activeElement = null; if (id) { this.open(id, extension, template) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts index 8a7c80af7c1..c5758eac3d6 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts @@ -55,7 +55,7 @@ export class InstallCountWidget extends ExtensionWidget { } render(): void { - this.container.innerHTML = ''; + this.container.innerText = ''; if (!this.extension) { return; @@ -105,7 +105,7 @@ export class RatingsWidget extends ExtensionWidget { } render(): void { - this.container.innerHTML = ''; + this.container.innerText = ''; if (!this.extension) { return; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index b4fbd1618f0..06d2ca5f2b2 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -438,7 +438,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR templateData.currentRenderedCell = element; templateData.currentEditor = undefined; templateData.editorPart!.style.display = 'none'; - templateData.cellContainer.innerHTML = ''; + templateData.cellContainer.innerText = ''; let renderedHTML = element.getHTML(); if (renderedHTML) { templateData.cellContainer.appendChild(renderedHTML); @@ -1109,7 +1109,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende return; } - templateData.outputContainer.innerHTML = ''; + templateData.outputContainer.innerText = ''; const elementDisposables = templateData.elementDisposables; @@ -1256,7 +1256,7 @@ export class RunStateRenderer { } }, RunStateRenderer.MIN_SPINNER_TIME); } else { - this.element.innerHTML = ''; + this.element.innerText = ''; } } } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts index 334187e139e..7831651c5bf 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts @@ -176,7 +176,7 @@ export class StatefulMarkdownCell extends Disposable { const lineHeight = this.viewCell.layoutInfo.fontInfo?.lineHeight || 17; editorHeight = Math.max(lineNum, 1) * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; - this.templateData.editorContainer.innerHTML = ''; + this.templateData.editorContainer.innerText = ''; // create a special context key service that set the inCompositeEditor-contextkey const editorContextKeyService = this.contextKeyService.createScoped(); @@ -239,7 +239,7 @@ export class StatefulMarkdownCell extends Disposable { this.renderedEditors.delete(this.viewCell); - this.markdownContainer.innerHTML = ''; + this.markdownContainer.innerText = ''; this.viewCell.clearHTML(); let markdownRenderer = this.viewCell.getMarkdownRenderer(); let renderedHTML = this.viewCell.getHTML(); @@ -259,7 +259,7 @@ export class StatefulMarkdownCell extends Disposable { })); this.localDisposables.add(this.viewCell.textBuffer.onDidChangeContent(() => { - this.markdownContainer.innerHTML = ''; + this.markdownContainer.innerText = ''; this.viewCell.clearHTML(); let renderedHTML = this.viewCell.getHTML(); if (renderedHTML) { @@ -310,7 +310,7 @@ export class StatefulMarkdownCell extends Disposable { setFoldingIndicator() { switch (this.foldingState) { case CellFoldingState.None: - this.templateData.foldingIndicator.innerHTML = ''; + this.templateData.foldingIndicator.innerText = ''; break; case CellFoldingState.Collapsed: this.templateData.foldingIndicator.innerHTML = renderCodicons('$(chevron-right)'); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 57dee431804..d74fd49be37 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -420,7 +420,7 @@ function webviewPreloads() { case 'clear': queuedOuputActions.clear(); // stop all loading outputs onWillDestroyOutput.fire([undefined, undefined]); - document.getElementById('container')!.innerHTML = ''; + document.getElementById('container')!.innerText = ''; outputObservers.forEach(ob => { ob.disconnect(); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index cdf45a9b21d..faaba04d362 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -636,7 +636,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre template.labelElement.textContent = element.displayLabel; template.labelElement.title = titleTooltip; - template.descriptionElement.innerHTML = ''; + template.descriptionElement.innerText = ''; if (element.setting.descriptionIsMarkdown) { const disposables = new DisposableStore(); template.toDispose.add(disposables); @@ -649,7 +649,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre const baseId = (element.displayCategory + '_' + element.displayLabel).replace(/ /g, '_').toLowerCase(); template.descriptionElement.id = baseId + '_setting_description'; - template.otherOverridesElement.innerHTML = ''; + template.otherOverridesElement.innerText = ''; template.otherOverridesElement.style.display = 'none'; if (element.overriddenScopeList.length) { template.otherOverridesElement.style.display = 'inline'; @@ -686,7 +686,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre if (deprecationText && element.setting.deprecationMessageIsMarkdown) { const disposables = new DisposableStore(); template.elementDisposables.add(disposables); - template.deprecationWarningElement.innerHTML = ''; + template.deprecationWarningElement.innerText = ''; template.deprecationWarningElement.appendChild(this.renderSettingMarkdown(element, element.setting.deprecationMessage!, template.elementDisposables)); } else { template.deprecationWarningElement.innerText = deprecationText; @@ -820,7 +820,7 @@ export class SettingGroupRenderer implements ITreeRenderer, index: number, templateData: IGroupTitleTemplate): void { - templateData.parent.innerHTML = ''; + templateData.parent.innerText = ''; const labelElement = DOM.append(templateData.parent, $('div.settings-group-title-label')); labelElement.classList.add(`settings-group-level-${element.element.level}`); labelElement.textContent = element.element.label; @@ -1365,7 +1365,7 @@ export class SettingEnumRenderer extends AbstractSettingRenderer implements ITre template.selectBox.select(idx); template.onChange = idx => onChange(dataElement.setting.enum![idx]); - template.enumDescriptionElement.innerHTML = ''; + template.enumDescriptionElement.innerText = ''; } } diff --git a/src/vs/workbench/test/browser/part.test.ts b/src/vs/workbench/test/browser/part.test.ts index a2bd94ea0ec..3fc67fcff6a 100644 --- a/src/vs/workbench/test/browser/part.test.ts +++ b/src/vs/workbench/test/browser/part.test.ts @@ -63,7 +63,7 @@ class MyPart2 extends SimplePart { const titleContainer = append(parent, $('div')); const titleLabel = append(titleContainer, $('span')); titleLabel.id = 'myPart.title'; - titleLabel.innerHTML = 'Title'; + titleLabel.innerText = 'Title'; return titleContainer; } @@ -72,7 +72,7 @@ class MyPart2 extends SimplePart { const contentContainer = append(parent, $('div')); const contentSpan = append(contentContainer, $('span')); contentSpan.id = 'myPart.content'; - contentSpan.innerHTML = 'Content'; + contentSpan.innerText = 'Content'; return contentContainer; } @@ -92,7 +92,7 @@ class MyPart3 extends SimplePart { const contentContainer = append(parent, $('div')); const contentSpan = append(contentContainer, $('span')); contentSpan.id = 'myPart.content'; - contentSpan.innerHTML = 'Content'; + contentSpan.innerText = 'Content'; return contentContainer; } From 0b10adc8bc21286eb6dde2b8360a0a0b99a7551e Mon Sep 17 00:00:00 2001 From: bamurtaugh Date: Mon, 3 Aug 2020 18:43:14 -0700 Subject: [PATCH 033/736] Add initial debug extension button --- .../contrib/debug/browser/debugService.ts | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 7bba378be0c..2c5323480df 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -46,6 +46,7 @@ import { generateUuid } from 'vs/base/common/uuid'; import { DebugStorage } from 'vs/workbench/contrib/debug/common/debugStorage'; import { DebugTelemetry } from 'vs/workbench/contrib/debug/common/debugTelemetry'; import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompoundRoot'; +import { IExtensionsViewPaneContainer, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; export class DebugService implements IDebugService { declare readonly _serviceBrand: undefined; @@ -432,7 +433,34 @@ export class DebugService implements IDebugService { nls.localize('debugTypeMissing', "Missing property 'type' for the chosen launch configuration."); } - await this.showError(message); + /*let actionList: IAction[] = []; + let action = { + id: "Install Debug Extension", + label: "Install Debug Extension(s)", + tooltip: "Install Debug Extension", + class: "string", + checked: true, + run: () => this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID), + dispose: () => this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID), + enabled: true + } as IAction; + actionList.push(action); + await this.showError(message, actionList); + return false;*/ + + let actionList: IAction[] = []; + let action = { + id: 'Install Debug Extension', + label: 'Install Debug Extension(s)', + tooltip: 'Install Debug Extension', + class: 'string', + checked: true, + run: () => this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true), + dispose: () => this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true), + enabled: true + } as IAction; + actionList.push(action); + await this.showError(message, actionList); return false; } From 6520fb2a80de350c7790aa15b26be002c14a9cd4 Mon Sep 17 00:00:00 2001 From: bamurtaugh Date: Mon, 3 Aug 2020 19:01:39 -0700 Subject: [PATCH 034/736] Filter extensions in viewlet --- .../contrib/debug/browser/debugService.ts | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 2c5323480df..53ece89282b 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -433,21 +433,6 @@ export class DebugService implements IDebugService { nls.localize('debugTypeMissing', "Missing property 'type' for the chosen launch configuration."); } - /*let actionList: IAction[] = []; - let action = { - id: "Install Debug Extension", - label: "Install Debug Extension(s)", - tooltip: "Install Debug Extension", - class: "string", - checked: true, - run: () => this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID), - dispose: () => this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID), - enabled: true - } as IAction; - actionList.push(action); - await this.showError(message, actionList); - return false;*/ - let actionList: IAction[] = []; let action = { id: 'Install Debug Extension', @@ -455,7 +440,11 @@ export class DebugService implements IDebugService { tooltip: 'Install Debug Extension', class: 'string', checked: true, - run: () => this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true), + run: async () => { + const viewlet = (await this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true))?.getViewPaneContainer() as IExtensionsViewPaneContainer; + viewlet.search('tag:debuggers @sort:installs'); + return this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true); + }, dispose: () => this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true), enabled: true } as IAction; From ef68d200f03822254e1d6b302ee3f5188e6bce9e Mon Sep 17 00:00:00 2001 From: bamurtaugh Date: Tue, 4 Aug 2020 19:53:17 -0700 Subject: [PATCH 035/736] First pass of optimizations --- .../contrib/debug/browser/debugService.ts | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 53ece89282b..dbdaa80335a 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -28,7 +28,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { parse, getFirstFrame } from 'vs/base/common/console'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IAction } from 'vs/base/common/actions'; +import { IAction, Action } from 'vs/base/common/actions'; import { deepClone, equals } from 'vs/base/common/objects'; import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; @@ -47,6 +47,7 @@ import { DebugStorage } from 'vs/workbench/contrib/debug/common/debugStorage'; import { DebugTelemetry } from 'vs/workbench/contrib/debug/common/debugTelemetry'; import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompoundRoot'; import { IExtensionsViewPaneContainer, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; +import { CommandService } from 'vs/workbench/services/commands/common/commandService'; export class DebugService implements IDebugService { declare readonly _serviceBrand: undefined; @@ -72,6 +73,7 @@ export class DebugService implements IDebugService { private previousState: State | undefined; private sessionCancellationTokens = new Map(); private activity: IDisposable | undefined; + private commandService!: CommandService; constructor( @IEditorService private readonly editorService: IEditorService, @@ -433,23 +435,23 @@ export class DebugService implements IDebugService { nls.localize('debugTypeMissing', "Missing property 'type' for the chosen launch configuration."); } - let actionList: IAction[] = []; - let action = { - id: 'Install Debug Extension', - label: 'Install Debug Extension(s)', - tooltip: 'Install Debug Extension', - class: 'string', - checked: true, - run: async () => { + const actionList: IAction[] = []; + + actionList.push(new Action( + 'installAdditionalDebuggers', + nls.localize('installAdditionalDebuggers', "Install {0} Extension", resolvedConfig.type), + undefined, + true, + async () => { const viewlet = (await this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true))?.getViewPaneContainer() as IExtensionsViewPaneContainer; viewlet.search('tag:debuggers @sort:installs'); return this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true); - }, - dispose: () => this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true), - enabled: true - } as IAction; - actionList.push(action); + } + //() => this.commandService.executeCommand('debug.installAdditionalDebuggers') + )); + await this.showError(message, actionList); + return false; } From fe1db063e72af4d7c95c489d1f8dbcba0a567fc2 Mon Sep 17 00:00:00 2001 From: Pascal Fong Kye Date: Wed, 5 Aug 2020 12:13:03 +0200 Subject: [PATCH 036/736] keep default flex values for children and use margins --- src/vs/workbench/contrib/debug/browser/media/repl.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/media/repl.css b/src/vs/workbench/contrib/debug/browser/media/repl.css index 1970134e9c3..b9599c28628 100644 --- a/src/vs/workbench/contrib/debug/browser/media/repl.css +++ b/src/vs/workbench/contrib/debug/browser/media/repl.css @@ -35,7 +35,7 @@ } .monaco-workbench .repl .repl-tree .output.expression.value-and-source .value { - flex: 1; + margin-right: 4px; } .monaco-workbench .repl .repl-tree .monaco-tl-contents .arrow { @@ -44,14 +44,14 @@ } .monaco-workbench .repl .repl-tree .output.expression.value-and-source .source { - margin-left: 4px; + margin-left: auto; margin-right: 8px; cursor: pointer; text-decoration: underline; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - max-width: 150px; + text-align: right; } .monaco-workbench .repl .repl-tree .output.expression > .value, From 9bfcfac290ccbafca364b9c9e04f80afc3801709 Mon Sep 17 00:00:00 2001 From: annkamsk Date: Mon, 3 Aug 2020 19:19:53 +0200 Subject: [PATCH 037/736] Replace `innerHTML` with `innerText` when assigning non-html string --- .../code/electron-sandbox/issue/issueReporterMain.ts | 10 +++++----- src/vs/editor/browser/config/charWidthReader.ts | 4 ++-- src/vs/editor/browser/widget/diffReview.ts | 8 ++++---- src/vs/editor/contrib/codelens/codelensWidget.ts | 2 +- src/vs/editor/test/browser/controller/imeTester.ts | 6 +++--- .../browser/parts/editor/breadcrumbsControl.ts | 2 +- .../welcome/walkThrough/browser/walkThroughPart.ts | 2 +- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts index c7c038286e0..1bfa1933252 100644 --- a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts @@ -713,7 +713,7 @@ export class IssueReporter extends Disposable { } } - sourceSelect.innerHTML = ''; + sourceSelect.innerText = ''; if (issueType === IssueType.FeatureRequest) { sourceSelect.append(...[ this.makeOption('', localize('selectSource', "Select source"), true), @@ -1079,7 +1079,7 @@ export class IssueReporter extends Disposable { } private updateExtensionTable(extensions: IssueReporterExtensionData[], numThemeExtensions: number): void { - const target = document.querySelector('.block-extensions .block-info'); + const target = document.querySelector('.block-extensions .block-info'); if (target) { if (this.configuration.disableExtensions) { target.innerHTML = localize('disabledExtensions', "Extensions are disabled"); @@ -1090,7 +1090,7 @@ export class IssueReporter extends Disposable { extensions = extensions || []; if (!extensions.length) { - target.innerHTML = 'Extensions: none' + themeExclusionStr; + target.innerText = 'Extensions: none' + themeExclusionStr; return; } @@ -1100,10 +1100,10 @@ export class IssueReporter extends Disposable { } private updateSearchedExtensionTable(extensions: IssueReporterExtensionData[]): void { - const target = document.querySelector('.block-searchedExtensions .block-info'); + const target = document.querySelector('.block-searchedExtensions .block-info'); if (target) { if (!extensions.length) { - target.innerHTML = 'Extensions: none'; + target.innerText = 'Extensions: none'; return; } diff --git a/src/vs/editor/browser/config/charWidthReader.ts b/src/vs/editor/browser/config/charWidthReader.ts index 479a35c7bea..97fe48dd732 100644 --- a/src/vs/editor/browser/config/charWidthReader.ts +++ b/src/vs/editor/browser/config/charWidthReader.ts @@ -124,12 +124,12 @@ class DomCharWidthReader { private static _render(testElement: HTMLElement, request: CharWidthRequest): void { if (request.chr === ' ') { - let htmlString = ' '; + let htmlString = '\u00a0'; // Repeat character 256 (2^8) times for (let i = 0; i < 8; i++) { htmlString += htmlString; } - testElement.innerHTML = htmlString; + testElement.innerText = htmlString; } else { let testString = request.chr; // Repeat character 256 (2^8) times diff --git a/src/vs/editor/browser/widget/diffReview.ts b/src/vs/editor/browser/widget/diffReview.ts index 3afb89e70a6..e6e1c34d987 100644 --- a/src/vs/editor/browser/widget/diffReview.ts +++ b/src/vs/editor/browser/widget/diffReview.ts @@ -702,7 +702,7 @@ export class DiffReview extends Disposable { if (originalLine !== 0) { originalLineNumber.appendChild(document.createTextNode(String(originalLine))); } else { - originalLineNumber.innerHTML = ' '; + originalLineNumber.innerText = '\u00a0'; } cell.appendChild(originalLineNumber); @@ -714,7 +714,7 @@ export class DiffReview extends Disposable { if (modifiedLine !== 0) { modifiedLineNumber.appendChild(document.createTextNode(String(modifiedLine))); } else { - modifiedLineNumber.innerHTML = ' '; + modifiedLineNumber.innerText = '\u00a0'; } cell.appendChild(modifiedLineNumber); @@ -724,10 +724,10 @@ export class DiffReview extends Disposable { if (spacerIcon) { const spacerCodicon = document.createElement('span'); spacerCodicon.className = spacerIcon.classNames; - spacerCodicon.innerHTML = '  '; + spacerCodicon.innerText = '\u00a0\u00a0'; spacer.appendChild(spacerCodicon); } else { - spacer.innerHTML = '  '; + spacer.innerText = '\u00a0\u00a0'; } cell.appendChild(spacer); diff --git a/src/vs/editor/contrib/codelens/codelensWidget.ts b/src/vs/editor/contrib/codelens/codelensWidget.ts index 6fe58d4ee80..8c05d953292 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/codelensWidget.ts @@ -108,7 +108,7 @@ class CodeLensContentWidget implements IContentWidget { } else { // symbols and commands if (!innerHtml) { - innerHtml = ' '; + innerHtml = '\u00a0'; } this._domNode.innerHTML = innerHtml; if (this._isEmpty && animate) { diff --git a/src/vs/editor/test/browser/controller/imeTester.ts b/src/vs/editor/test/browser/controller/imeTester.ts index 1668233bf9d..d1d625087f8 100644 --- a/src/vs/editor/test/browser/controller/imeTester.ts +++ b/src/vs/editor/test/browser/controller/imeTester.ts @@ -74,7 +74,7 @@ function doCreateTest(description: string, inputStr: string, expectedStr: string container.appendChild(title); let startBtn = document.createElement('button'); - startBtn.innerHTML = 'Start'; + startBtn.innerText = 'Start'; container.appendChild(startBtn); @@ -141,10 +141,10 @@ function doCreateTest(description: string, inputStr: string, expectedStr: string let expected = 'some ' + expectedStr + ' text'; if (text === expected) { - check.innerHTML = '[GOOD]'; + check.innerText = '[GOOD]'; check.className = 'check good'; } else { - check.innerHTML = '[BAD]'; + check.innerText = '[BAD]'; check.className = 'check bad'; } check.innerHTML += expected; diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index eca511ebe7b..235730230bd 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -98,7 +98,7 @@ class Item extends BreadcrumbsItem { } else if (this.element instanceof OutlineModel) { // has outline element but not in one let label = document.createElement('div'); - label.innerHTML = '…'; + label.innerText = '\u2026'; label.className = 'hint-more'; container.appendChild(label); diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts index d4378989b5f..4d73d5db9cf 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts @@ -268,7 +268,7 @@ export class WalkThroughPart extends BaseEditor { } this.contentDisposables = dispose(this.contentDisposables); - this.content.innerHTML = ''; + this.content.innerText = ''; return super.setInput(input, options, token) .then(() => { From c6bed3905d73512cee6b6623cf86e10d9fb36aad Mon Sep 17 00:00:00 2001 From: Ikko Ashimine Date: Fri, 7 Aug 2020 00:40:31 +0900 Subject: [PATCH 038/736] Fixed typo 'occured' to 'occurred' * message --- src/vs/platform/remote/common/remoteAgentConnection.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/remote/common/remoteAgentConnection.ts b/src/vs/platform/remote/common/remoteAgentConnection.ts index 2185bb5228c..b8b6b2c35a9 100644 --- a/src/vs/platform/remote/common/remoteAgentConnection.ts +++ b/src/vs/platform/remote/common/remoteAgentConnection.ts @@ -453,24 +453,24 @@ abstract class PersistentConnection extends Disposable { break; } if (RemoteAuthorityResolverError.isTemporarilyNotAvailable(err)) { - this._options.logService.info(`${logPrefix} A temporarily not available error occured while trying to reconnect, will try again...`); + this._options.logService.info(`${logPrefix} A temporarily not available error occurred while trying to reconnect, will try again...`); this._options.logService.trace(err); // try again! continue; } if ((err.code === 'ETIMEDOUT' || err.code === 'ENETUNREACH' || err.code === 'ECONNREFUSED' || err.code === 'ECONNRESET') && err.syscall === 'connect') { - this._options.logService.info(`${logPrefix} A network error occured while trying to reconnect, will try again...`); + this._options.logService.info(`${logPrefix} A network error occurred while trying to reconnect, will try again...`); this._options.logService.trace(err); // try again! continue; } if (isPromiseCanceledError(err)) { - this._options.logService.info(`${logPrefix} A promise cancelation error occured while trying to reconnect, will try again...`); + this._options.logService.info(`${logPrefix} A promise cancelation error occurred while trying to reconnect, will try again...`); this._options.logService.trace(err); // try again! continue; } - this._options.logService.error(`${logPrefix} An unknown error occured while trying to reconnect, since this is an unknown case, it will be treated as a permanent error! Will give up now! Error:`); + this._options.logService.error(`${logPrefix} An unknown error occurred while trying to reconnect, since this is an unknown case, it will be treated as a permanent error! Will give up now! Error:`); this._options.logService.error(err); PersistentConnection.triggerPermanentFailure(); break; From af7765c06ff62ee48c5dd7df51670d8fc79f6df8 Mon Sep 17 00:00:00 2001 From: Stepan Repin Date: Sat, 8 Aug 2020 21:19:37 +0300 Subject: [PATCH 039/736] Add 'trailing' option to render whitespace --- .../browser/viewParts/lines/viewLine.ts | 2 +- src/vs/editor/common/config/editorOptions.ts | 7 +- .../common/viewLayout/viewLineRenderer.ts | 24 ++++- .../viewLayout/viewLineRenderer.test.ts | 91 ++++++++++++++++++- src/vs/monaco.d.ts | 4 +- 5 files changed, 116 insertions(+), 12 deletions(-) diff --git a/src/vs/editor/browser/viewParts/lines/viewLine.ts b/src/vs/editor/browser/viewParts/lines/viewLine.ts index 46d36912654..65798b74acc 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLine.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLine.ts @@ -72,7 +72,7 @@ export class DomReadingContext { export class ViewLineOptions { public readonly themeType: ThemeType; - public readonly renderWhitespace: 'none' | 'boundary' | 'selection' | 'all'; + public readonly renderWhitespace: 'none' | 'boundary' | 'selection' | 'trailing' | 'all'; public readonly renderControlCharacters: boolean; public readonly spaceWidth: number; public readonly middotWidth: number; diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 6176dcd8fa0..e8c043400b2 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -529,7 +529,7 @@ export interface IEditorOptions { * Enable rendering of whitespace. * Defaults to none. */ - renderWhitespace?: 'none' | 'boundary' | 'selection' | 'all'; + renderWhitespace?: 'none' | 'boundary' | 'selection' | 'trailing' | 'all'; /** * Enable rendering of control characters. * Defaults to false. @@ -4037,13 +4037,14 @@ export const EditorOptions = { )), renderWhitespace: register(new EditorStringEnumOption( EditorOption.renderWhitespace, 'renderWhitespace', - 'selection' as 'selection' | 'none' | 'boundary' | 'all', - ['none', 'boundary', 'selection', 'all'] as const, + 'selection' as 'selection' | 'none' | 'boundary' | 'trailing' | 'all', + ['none', 'boundary', 'selection', 'trailing', 'all'] as const, { enumDescriptions: [ '', nls.localize('renderWhitespace.boundary', "Render whitespace characters except for single spaces between words."), nls.localize('renderWhitespace.selection', "Render whitespace characters only on selected text."), + nls.localize('renderWhitespace.trailing', "Render only trailing whitespace characters"), '' ], description: nls.localize('renderWhitespace', "Controls how the editor should render whitespace characters.") diff --git a/src/vs/editor/common/viewLayout/viewLineRenderer.ts b/src/vs/editor/common/viewLayout/viewLineRenderer.ts index 3ff97b83674..23640e0af3b 100644 --- a/src/vs/editor/common/viewLayout/viewLineRenderer.ts +++ b/src/vs/editor/common/viewLayout/viewLineRenderer.ts @@ -14,7 +14,8 @@ export const enum RenderWhitespace { None = 0, Boundary = 1, Selection = 2, - All = 3 + Trailing = 3, + All = 4 } export const enum LinePartMetadata { @@ -113,7 +114,7 @@ export class RenderLineInput { middotWidth: number, wsmiddotWidth: number, stopRenderingLineAfter: number, - renderWhitespace: 'none' | 'boundary' | 'selection' | 'all', + renderWhitespace: 'none' | 'boundary' | 'selection' | 'trailing' | 'all', renderControlCharacters: boolean, fontLigatures: boolean, selectionsOnLine: LineRange[] | null @@ -138,7 +139,9 @@ export class RenderLineInput { ? RenderWhitespace.Boundary : renderWhitespace === 'selection' ? RenderWhitespace.Selection - : RenderWhitespace.None + : renderWhitespace === 'trailing' + ? RenderWhitespace.Trailing + : RenderWhitespace.None ); this.renderControlCharacters = renderControlCharacters; this.fontLigatures = fontLigatures; @@ -435,7 +438,11 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput } let tokens = transformAndRemoveOverflowing(input.lineTokens, input.fauxIndentLength, len); - if (input.renderWhitespace === RenderWhitespace.All || input.renderWhitespace === RenderWhitespace.Boundary || (input.renderWhitespace === RenderWhitespace.Selection && !!input.selectionsOnLine)) { + if (input.renderWhitespace === RenderWhitespace.All || + input.renderWhitespace === RenderWhitespace.Boundary || + (input.renderWhitespace === RenderWhitespace.Selection && !!input.selectionsOnLine) || + input.renderWhitespace === RenderWhitespace.Trailing) { + tokens = _applyRenderWhitespace(input, lineContent, len, tokens); } let containsForeignElements = ForeignElementType.None; @@ -592,6 +599,7 @@ function _applyRenderWhitespace(input: RenderLineInput, lineContent: string, len const useMonospaceOptimizations = input.useMonospaceOptimizations; const selections = input.selectionsOnLine; const onlyBoundary = (input.renderWhitespace === RenderWhitespace.Boundary); + const onlyTrailing = (input.renderWhitespace === RenderWhitespace.Trailing); const generateLinePartForEachWhitespace = (input.renderSpaceWidth !== input.spaceWidth); let result: LinePart[] = [], resultLen = 0; @@ -600,10 +608,11 @@ function _applyRenderWhitespace(input: RenderLineInput, lineContent: string, len let tokenEndIndex = tokens[tokenIndex].endIndex; const tokensLength = tokens.length; + let lineIsEmptyOrWhitespace = false; let firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent); let lastNonWhitespaceIndex: number; if (firstNonWhitespaceIndex === -1) { - // The entire line is whitespace + lineIsEmptyOrWhitespace = true; firstNonWhitespaceIndex = len; lastNonWhitespaceIndex = len; } else { @@ -651,6 +660,11 @@ function _applyRenderWhitespace(input: RenderLineInput, lineContent: string, len isInWhitespace = !!currentSelection && currentSelection.startOffset <= charIndex && currentSelection.endOffset > charIndex; } + // If rendering only trailing whitespace, check that the charIndex points to trailing whitespace. + if (isInWhitespace && onlyTrailing) { + isInWhitespace = lineIsEmptyOrWhitespace || charIndex > lastNonWhitespaceIndex; + } + if (wasInWhitespace) { // was in whitespace token if (!isInWhitespace || (!useMonospaceOptimizations && tmpIndent >= tabSize)) { diff --git a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts index 1147b17c205..0e0dc7d3d63 100644 --- a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts +++ b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts @@ -869,7 +869,7 @@ suite('viewLineRenderer.renderLine', () => { suite('viewLineRenderer.renderLine 2', () => { - function testCreateLineParts(fontIsMonospace: boolean, lineContent: string, tokens: ViewLineToken[], fauxIndentLength: number, renderWhitespace: 'none' | 'boundary' | 'selection' | 'all', selections: LineRange[] | null, expected: string): void { + function testCreateLineParts(fontIsMonospace: boolean, lineContent: string, tokens: ViewLineToken[], fauxIndentLength: number, renderWhitespace: 'none' | 'boundary' | 'selection' | 'trailing' | 'all', selections: LineRange[] | null, expected: string): void { let actual = renderViewLine(new RenderLineInput( fontIsMonospace, true, @@ -1355,6 +1355,95 @@ suite('viewLineRenderer.renderLine 2', () => { ); }); + test('createLineParts render whitespace for trailing with leading, inner, and without trailing whitespace', () => { + testCreateLineParts( + false, + ' Hello world!', + [ + createPart(4, 0), + createPart(6, 1), + createPart(14, 2) + ], + 0, + 'trailing', + null, + [ + '', + '\u00a0Hel', + 'lo', + '\u00a0world!', + '', + ].join('') + ); + }); + + test('createLineParts render whitespace for trailing with leading, inner, and trailing whitespace', () => { + testCreateLineParts( + false, + ' Hello world! \t', + [ + createPart(4, 0), + createPart(6, 1), + createPart(15, 2) + ], + 0, + 'trailing', + null, + [ + '', + '\u00a0Hel', + 'lo', + '\u00a0world!', + '\u00b7\u2192\u00a0', + '', + ].join('') + ); + }); + + test('createLineParts render whitespace for trailing with 8 leading and 8 trailing whitespaces', () => { + testCreateLineParts( + false, + ' Hello world! ', + [ + createPart(8, 1), + createPart(10, 2), + createPart(28, 3) + ], + 0, + 'trailing', + null, + [ + '', + '\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0', + 'He', + 'llo\u00a0world!', + '\u00b7\u00b7\u00b7\u00b7', + '\u00b7\u00b7\u00b7\u00b7', + '', + ].join('') + ); + }); + + test('createLineParts render whitespace for trailing with line containing only whitespaces', () => { + testCreateLineParts( + false, + ' \t ', + [ + createPart(2, 0), + createPart(3, 1), + ], + 0, + 'trailing', + null, + [ + '', + '\u00b7\u2192\u00a0\u00a0', + '\u00b7', + '', + ].join('') + ); + }); + test('createLineParts can handle unsorted inline decorations', () => { let actual = renderViewLine(new RenderLineInput( false, diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 0593f65eb4a..4ef0449f03f 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3068,7 +3068,7 @@ declare namespace monaco.editor { * Enable rendering of whitespace. * Defaults to none. */ - renderWhitespace?: 'none' | 'boundary' | 'selection' | 'all'; + renderWhitespace?: 'none' | 'boundary' | 'selection' | 'trailing' | 'all'; /** * Enable rendering of control characters. * Defaults to false. @@ -4048,7 +4048,7 @@ declare namespace monaco.editor { renderLineHighlight: IEditorOption; renderLineHighlightOnlyWhenFocus: IEditorOption; renderValidationDecorations: IEditorOption; - renderWhitespace: IEditorOption; + renderWhitespace: IEditorOption; revealHorizontalRightPadding: IEditorOption; roundedSelection: IEditorOption; rulers: IEditorOption; From d36c19e19fdd920d5bd73e8ec76dc459a12bad64 Mon Sep 17 00:00:00 2001 From: Chuang Yu Date: Tue, 4 Aug 2020 21:09:54 +0800 Subject: [PATCH 040/736] fix #102718, add formatting indicator --- src/vs/editor/contrib/format/formatActions.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index c2eed67eab0..229272931b6 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -25,7 +25,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { onUnexpectedError } from 'vs/base/common/errors'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { Progress } from 'vs/platform/progress/common/progress'; +import { Progress, IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; class FormatOnType implements IEditorContribution { @@ -231,7 +231,13 @@ class FormatDocumentAction extends EditorAction { async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { if (editor.hasModel()) { const instaService = accessor.get(IInstantiationService); - await instaService.invokeFunction(formatDocumentWithSelectedProvider, editor, FormattingMode.Explicit, Progress.None, CancellationToken.None); + const progressService = accessor.get(IProgressService); + return progressService.withProgress({ + location: ProgressLocation.Window, + title: nls.localize("formatDocument.label.formatting", "Formatting document...") + }, async () => { + return await instaService.invokeFunction(formatDocumentWithSelectedProvider, editor, FormattingMode.Explicit, Progress.None, CancellationToken.None); + }); } } } @@ -267,7 +273,14 @@ class FormatSelectionAction extends EditorAction { if (range.isEmpty()) { range = new Range(range.startLineNumber, 1, range.startLineNumber, model.getLineMaxColumn(range.startLineNumber)); } - await instaService.invokeFunction(formatDocumentRangeWithSelectedProvider, editor, range, FormattingMode.Explicit, CancellationToken.None); + + const progressService = accessor.get(IProgressService); + return progressService.withProgress({ + location: ProgressLocation.Window, + title: nls.localize("formatSelection.label.formatting", "Formatting selection...") + }, async () => { + return await instaService.invokeFunction(formatDocumentRangeWithSelectedProvider, editor, range, FormattingMode.Explicit, CancellationToken.None); + }); } } From 406315dfec3e17f6f32e4e1a5b21c59873db6b05 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Mon, 10 Aug 2020 00:08:50 -0700 Subject: [PATCH 041/736] Disable bot for joao (vacation). --- .github/classifier.json | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/classifier.json b/.github/classifier.json index bb313eebafb..420cce5bfaf 100644 --- a/.github/classifier.json +++ b/.github/classifier.json @@ -1,6 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/microsoft/vscode-github-triage-actions/master/classifier-deep/apply/apply-labels/deep-classifier-config.schema.json", "assignees": { + "joaomoreno": {"accuracy": 1.5}, "JacksonKearl": {"accuracy": 0.5} }, "labels": { @@ -71,10 +72,10 @@ "file-watcher": {"assign": ["bpasero"]}, "font-rendering": {"assign": []}, "formatting": {"assign": []}, - "git": {"assign": ["joaomoreno"]}, + "git": {"assign": ["joaomoreno"], "accuracy": 1.5}, "gpu": {"assign": ["deepak1556"]}, "grammar": {"assign": ["mjbvz"]}, - "grid-view": {"assign": ["joaomoreno"]}, + "grid-view": {"assign": ["joaomoreno"], "accuracy": 1.5}, "html": {"assign": ["aeschli"]}, "i18n": {"assign": []}, "icon-brand": {"assign": []}, @@ -85,7 +86,7 @@ "integrated-terminal-links": {"assign": ["Tyriar"]}, "integration-test": {"assign": []}, "intellisense-config": {"assign": []}, - "ipc": {"assign": ["joaomoreno"]}, + "ipc": {"assign": ["joaomoreno"], "accuracy": 1.5}, "issue-bot": {"assign": ["chrmarti"]}, "issue-reporter": {"assign": ["RMacfarlane"]}, "javascript": {"assign": ["mjbvz"]}, @@ -97,7 +98,7 @@ "languages-diagnostics": {"assign": ["jrieken"]}, "layout": {"assign": ["sbatten"]}, "lcd-text-rendering": {"assign": []}, - "list": {"assign": ["joaomoreno"]}, + "list": {"assign": ["joaomoreno"], "accuracy": 1.5}, "log": {"assign": []}, "markdown": {"assign": ["mjbvz"]}, "marketplace": {"assign": []}, @@ -110,7 +111,7 @@ "perf-bloat": {"assign": []}, "perf-startup": {"assign": []}, "php": {"assign": ["roblourens"]}, - "portable-mode": {"assign": ["joaomoreno"]}, + "portable-mode": {"assign": ["joaomoreno"], "accuracy": 1.5}, "proxy": {"assign": []}, "quick-pick": {"assign": ["chrmarti"]}, "references-viewlet": {"assign": ["jrieken"]}, @@ -118,7 +119,7 @@ "remote": {"assign": []}, "remote-explorer": {"assign": ["alexr00"]}, "rename": {"assign": ["jrieken"]}, - "scm": {"assign": ["joaomoreno"]}, + "scm": {"assign": ["joaomoreno"], "accuracy": 1.5}, "screencast-mode": {"assign": ["lszomoru"]}, "search": {"assign": ["roblourens"]}, "search-editor": {"assign": ["JacksonKearl"]}, @@ -129,9 +130,9 @@ "simple-file-dialog": {"assign": ["alexr00"]}, "smart-select": {"assign": ["jrieken"]}, "smoke-test": {"assign": []}, - "snap": {"assign": ["joaomoreno"]}, + "snap": {"assign": ["joaomoreno"], "accuracy": 1.5}, "snippets": {"assign": ["jrieken"]}, - "splitview": {"assign": ["joaomoreno"]}, + "splitview": {"assign": ["joaomoreno"], "accuracy": 1.5}, "suggest": {"assign": ["jrieken"]}, "tasks": {"assign": ["alexr00"]}, "telemetry": {"assign": []}, @@ -140,7 +141,7 @@ "timeline-git": {"assign": ["eamodio"]}, "titlebar": {"assign": ["sbatten"]}, "tokenization": {"assign": []}, - "tree": {"assign": ["joaomoreno"]}, + "tree": {"assign": ["joaomoreno"], "accuracy": 1.5}, "typescript": {"assign": ["mjbvz"]}, "undo-redo": {"assign": []}, "unit-test": {"assign": []}, From a5570d196115db6cedfd83ffd8fc807c2bc36ae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 10 Aug 2020 10:55:07 +0200 Subject: [PATCH 042/736] introduce scm view service --- src/vs/workbench/contrib/scm/browser/menus.ts | 43 +++--- .../contrib/scm/browser/scm.contribution.ts | 4 +- .../contrib/scm/browser/scmViewPane.ts | 124 +++++++++--------- src/vs/workbench/contrib/scm/common/scm.ts | 14 ++ .../contrib/scm/common/scmViewService.ts | 82 ++++++++++++ 5 files changed, 178 insertions(+), 89 deletions(-) create mode 100644 src/vs/workbench/contrib/scm/common/scmViewService.ts diff --git a/src/vs/workbench/contrib/scm/browser/menus.ts b/src/vs/workbench/contrib/scm/browser/menus.ts index 500f7fa585a..68f93b71938 100644 --- a/src/vs/workbench/contrib/scm/browser/menus.ts +++ b/src/vs/workbench/contrib/scm/browser/menus.ts @@ -10,9 +10,9 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; import { IAction } from 'vs/base/common/actions'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { ISCMResource, ISCMResourceGroup, ISCMProvider, ISCMRepository } from 'vs/workbench/contrib/scm/common/scm'; +import { ISCMResource, ISCMResourceGroup, ISCMProvider, ISCMRepository, ISCMService } from 'vs/workbench/contrib/scm/common/scm'; import { equals } from 'vs/base/common/arrays'; -import { ISplice, ISequence } from 'vs/base/common/sequence'; +import { ISplice } from 'vs/base/common/sequence'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; @@ -237,44 +237,37 @@ export class SCMMenus { readonly titleMenu: SCMTitleMenu; private readonly disposables = new DisposableStore(); - private readonly entries: { repository: ISCMRepository, dispose: () => void }[] = []; - private readonly menus = new Map(); + private readonly menus = new Map void }>(); constructor( - repositories: ISequence, + @ISCMService scmService: ISCMService, @IInstantiationService private instantiationService: IInstantiationService ) { this.titleMenu = instantiationService.createInstance(SCMTitleMenu); + scmService.onDidRemoveRepository(this.onDidRemoveRepository, this, this.disposables); + } - repositories.onDidSplice(this.onDidSplice, this, this.disposables); - this.onDidSplice({ start: 0, deleteCount: 0, toInsert: repositories.elements }); + private onDidRemoveRepository(repository: ISCMRepository): void { + const menus = this.menus.get(repository.provider); + menus?.dispose(); + this.menus.delete(repository.provider); } getRepositoryMenus(provider: ISCMProvider): SCMRepositoryMenus { - if (!this.menus.has(provider)) { - throw new Error('SCM Repository menu not found'); - } + let result = this.menus.get(provider); - return this.menus.get(provider)!; - } - - private onDidSplice({ start, deleteCount, toInsert }: ISplice): void { - const entriesToInsert = toInsert.map(repository => { - const menus = this.instantiationService.createInstance(SCMRepositoryMenus, repository.provider); + if (!result) { + const menus = this.instantiationService.createInstance(SCMRepositoryMenus, provider); const dispose = () => { menus.dispose(); - this.menus.delete(repository.provider); + this.menus.delete(provider); }; - this.menus.set(repository.provider, menus); - return { repository, dispose }; - }); - - const deletedEntries = this.entries.splice(start, deleteCount, ...entriesToInsert); - - for (const entry of deletedEntries) { - entry.dispose(); + result = { menus, dispose }; + this.menus.set(provider, result); } + + return result.menus; } dispose(): void { diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index b0cda5d05ad..f522e31c5a2 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { DirtyDiffWorkbenchController } from './dirtydiffDecorator'; -import { VIEWLET_ID, ISCMRepository, ISCMService, VIEW_PANE_ID, ISCMProvider } from 'vs/workbench/contrib/scm/common/scm'; +import { VIEWLET_ID, ISCMRepository, ISCMService, VIEW_PANE_ID, ISCMProvider, ISCMViewService } from 'vs/workbench/contrib/scm/common/scm'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { SCMStatusController } from './activity'; @@ -24,6 +24,7 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; import { Codicon } from 'vs/base/common/codicons'; import { SCMViewPane } from 'vs/workbench/contrib/scm/browser/scmViewPane'; +import { SCMViewService } from 'vs/workbench/contrib/scm/common/scmViewService'; ModesRegistry.registerLanguage({ id: 'scminput', @@ -225,3 +226,4 @@ MenuRegistry.appendMenuItem(MenuId.SCMSourceControl, { }); registerSingleton(ISCMService, SCMService); +registerSingleton(ISCMViewService, SCMViewService); diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index faf74c03613..784ab9c1500 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -6,11 +6,11 @@ import 'vs/css!./media/scm'; import { Event, Emitter } from 'vs/base/common/event'; import { basename, dirname, isEqual } from 'vs/base/common/resources'; -import { IDisposable, Disposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable, DisposableStore, combinedDisposable, dispose } from 'vs/base/common/lifecycle'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { append, $, addClass, toggleClass, removeClass, Dimension } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { ISCMResourceGroup, ISCMResource, InputValidationType, ISCMService, ISCMRepository, ISCMInput, IInputValidation } from 'vs/workbench/contrib/scm/common/scm'; +import { ISCMResourceGroup, ISCMResource, InputValidationType, ISCMService, ISCMRepository, ISCMInput, IInputValidation, ISCMViewService, ISCMViewVisibleRepositoryChangeEvent } from 'vs/workbench/contrib/scm/common/scm'; import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -31,7 +31,7 @@ import { IConfigurationService, ConfigurationTarget, IConfigurationChangeEvent } import { disposableTimeout, ThrottledDelayer } from 'vs/base/common/async'; import { ITreeNode, ITreeFilter, ITreeSorter, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; import { ResourceTree, IResourceNode } from 'vs/base/common/resourceTree'; -import { ISequence, ISplice, SimpleSequence } from 'vs/base/common/sequence'; +import { ISplice } from 'vs/base/common/sequence'; import { ICompressibleTreeRenderer, ICompressibleKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/tree/objectTree'; import { Iterable } from 'vs/base/common/iterator'; import { ICompressedTreeNode, ICompressedTreeElement } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; @@ -803,13 +803,13 @@ interface IGroupItem { readonly element: ISCMResourceGroup; readonly resources: ISCMResource[]; readonly tree: ResourceTree; - readonly disposable: IDisposable; + dispose(): void; } interface IRepositoryItem { readonly element: ISCMRepository; readonly groupItems: IGroupItem[]; - readonly disposable: IDisposable; + dispose(): void; } function isRepositoryItem(item: IRepositoryItem | IGroupItem): item is IRepositoryItem { @@ -848,7 +848,7 @@ class ViewModel { set mode(mode: ViewModelMode) { this._mode = mode; - for (const item of this.items) { + for (const [, item] of this.items) { for (const groupItem of item.groupItems) { groupItem.tree.clear(); @@ -872,7 +872,7 @@ class ViewModel { } } - private items: IRepositoryItem[] = []; + private items = new Map(); private visibilityDisposables = new DisposableStore(); private scrollTop: number | undefined; private alwaysShowRepositories = false; @@ -882,7 +882,6 @@ class ViewModel { private disposables = new DisposableStore(); constructor( - readonly repositories: ISequence, private tree: WorkbenchCompressibleObjectTree, private menus: SCMMenus, private inputRenderer: InputRenderer, @@ -890,6 +889,7 @@ class ViewModel { private _sortKey: ViewModelSortKey, @IEditorService protected editorService: IEditorService, @IConfigurationService protected configurationService: IConfigurationService, + @ISCMViewService private scmViewService: ISCMViewService ) { this.onDidChangeRepositoryCollapseState = Event.any( this._onDidChangeRepositoryCollapseState.event, @@ -907,26 +907,27 @@ class ViewModel { } } - private _onDidSpliceRepositories({ start, deleteCount, toInsert }: ISplice): void { - const itemsToInsert = toInsert.map(repository => { + private _onDidChangeVisibleRepositories({ added, removed }: ISCMViewVisibleRepositoryChangeEvent): void { + for (const repository of added) { const disposable = combinedDisposable( repository.provider.groups.onDidSplice(splice => this._onDidSpliceGroups(item, splice)), repository.input.onDidChangeVisibility(() => this.refresh(item)) ); const groupItems = repository.provider.groups.elements.map(group => this.createGroupItem(group)); - const item: IRepositoryItem = { element: repository, groupItems, disposable }; + const item: IRepositoryItem = { + element: repository, groupItems, dispose() { + dispose(this.groupItems); + disposable.dispose(); + } + }; - return item; - }); + this.items.set(repository, item); + } - const itemsToDispose = this.items.splice(start, deleteCount, ...itemsToInsert); - - for (const item of itemsToDispose) { - for (const groupItem of item.groupItems) { - groupItem.disposable.dispose(); - } - - item.disposable.dispose(); + for (const repository of removed) { + const item = this.items.get(repository)!; + item.dispose(); + this.items.delete(repository); } this.refresh(); @@ -937,7 +938,7 @@ class ViewModel { const itemsToDispose = item.groupItems.splice(start, deleteCount, ...itemsToInsert); for (const item of itemsToDispose) { - item.disposable.dispose(); + item.dispose(); } this.refresh(); @@ -951,7 +952,7 @@ class ViewModel { group.onDidSplice(splice => this._onDidSpliceGroup(item, splice)) ); - const item: IGroupItem = { element: group, resources, tree, disposable }; + const item: IGroupItem = { element: group, resources, tree, dispose() { disposable.dispose(); } }; if (this._mode === ViewModelMode.Tree) { for (const resource of resources) { @@ -987,8 +988,8 @@ class ViewModel { setVisible(visible: boolean): void { if (visible) { this.visibilityDisposables = new DisposableStore(); - this.repositories.onDidSplice(this._onDidSpliceRepositories, this, this.visibilityDisposables); - this._onDidSpliceRepositories({ start: 0, deleteCount: 0, toInsert: this.repositories.elements }); + this.scmViewService.onDidChangeVisibleRepositories(this._onDidChangeVisibleRepositories, this, this.visibilityDisposables); + this._onDidChangeVisibleRepositories({ added: this.scmViewService.visibleRepositories, removed: Iterable.empty() }); this.repositoryCollapseStates = undefined; if (typeof this.scrollTop === 'number') { @@ -999,16 +1000,16 @@ class ViewModel { this.editorService.onDidActiveEditorChange(this.onDidActiveEditorChange, this, this.visibilityDisposables); this.onDidActiveEditorChange(); } else { - if (this.items.length > 1) { + if (this.items.size > 1) { this.repositoryCollapseStates = new Map(); - for (const item of this.items) { + for (const [, item] of this.items) { this.repositoryCollapseStates.set(item.element, this.tree.isCollapsed(item.element)); } } this.visibilityDisposables.dispose(); - this._onDidSpliceRepositories({ start: 0, deleteCount: this.items.length, toInsert: [] }); + this._onDidChangeVisibleRepositories({ added: Iterable.empty(), removed: [...this.items.keys()] }); this.scrollTop = this.tree.scrollTop; } @@ -1019,12 +1020,14 @@ class ViewModel { private refresh(item?: IRepositoryItem | IGroupItem): void { const focusedInput = this.inputRenderer.getFocusedInput(); - if (!this.alwaysShowRepositories && (this.items.length === 1 && (!item || isRepositoryItem(item)))) { - this.tree.setChildren(null, this.render(this.items[0]).children); + if (!this.alwaysShowRepositories && (this.items.size === 1 && (!item || isRepositoryItem(item)))) { + const item = Iterable.first(this.items.values())!; + this.tree.setChildren(null, this.render(item).children); } else if (item) { this.tree.setChildren(item.element, this.render(item).children); } else { - this.tree.setChildren(null, this.items.map(item => this.render(item))); + const items = this.scmViewService.visibleRepositories.map(r => this.items.get(r)!); + this.tree.setChildren(null, items.map(item => this.render(item))); } if (focusedInput) { @@ -1043,7 +1046,7 @@ class ViewModel { const children: ICompressedTreeElement[] = []; const hasSomeChanges = item.groupItems.some(item => item.element.elements.length > 0); - if (this.items.length === 1 || hasSomeChanges) { + if (this.items.size === 1 || hasSomeChanges) { if (item.element.input.visible) { children.push({ element: item.element.input, incompressible: true, collapsible: false }); } @@ -1085,8 +1088,9 @@ class ViewModel { return; } - for (let i = 0; i < this.items.length; i++) { - const item = this.items[i]; + for (const repository of this.scmViewService.visibleRepositories) { + const item = this.items.get(repository)!; + // go backwards from last group for (let j = item.groupItems.length - 1; j >= 0; j--) { const groupItem = item.groupItems[j]; @@ -1105,7 +1109,7 @@ class ViewModel { } focus() { - for (const repository of this.repositories.elements) { + for (const repository of this.scmViewService.visibleRepositories) { const widget = this.inputRenderer.getRenderedInputWidget(repository.input); if (widget) { @@ -1118,20 +1122,20 @@ class ViewModel { } getViewActions(): IAction[] { - if (this.repositories.elements.length === 0) { + if (this.scmViewService.visibleRepositories.length === 0) { return this.menus.titleMenu.actions; } - if (this.alwaysShowRepositories || this.repositories.elements.length !== 1) { + if (this.alwaysShowRepositories || this.scmViewService.visibleRepositories.length !== 1) { return []; } - const menus = this.menus.getRepositoryMenus(this.repositories.elements[0].provider); + const menus = this.menus.getRepositoryMenus(this.scmViewService.visibleRepositories[0].provider); return menus.titleMenu.actions; } getViewSecondaryActions(): IAction[] { - if (this.repositories.elements.length === 0) { + if (this.scmViewService.visibleRepositories.length === 0) { return this.menus.titleMenu.secondaryActions; } @@ -1140,11 +1144,11 @@ class ViewModel { this.disposables.add(this.viewSubMenuAction); } - if (this.alwaysShowRepositories || this.repositories.elements.length !== 1) { + if (this.alwaysShowRepositories || this.scmViewService.visibleRepositories.length !== 1) { return this.viewSubMenuAction.actions; } - const menus = this.menus.getRepositoryMenus(this.repositories.elements[0].provider); + const menus = this.menus.getRepositoryMenus(this.scmViewService.visibleRepositories[0].provider); const secondaryActions = menus.titleMenu.secondaryActions; if (secondaryActions.length === 0) { @@ -1155,19 +1159,19 @@ class ViewModel { } getViewActionsContext(): any { - if (this.repositories.elements.length === 0) { + if (this.scmViewService.visibleRepositories.length === 0) { return []; } - if (this.alwaysShowRepositories || this.repositories.elements.length !== 1) { + if (this.alwaysShowRepositories || this.scmViewService.visibleRepositories.length !== 1) { return undefined; } - return this.repositories.elements[0].provider; + return this.scmViewService.visibleRepositories[0].provider; } collapseAllProviders(): void { - for (const repository of this.repositories.elements) { + for (const repository of this.scmViewService.visibleRepositories) { if (this.tree.isCollapsible(repository)) { this.tree.collapse(repository); } @@ -1175,7 +1179,7 @@ class ViewModel { } expandAllProviders(): void { - for (const repository of this.repositories.elements) { + for (const repository of this.scmViewService.visibleRepositories) { if (this.tree.isCollapsible(repository)) { this.tree.expand(repository); } @@ -1183,30 +1187,26 @@ class ViewModel { } isAnyProviderCollapsible(): boolean { - if (!this.visible || this.repositories.elements.length === 1) { + if (!this.visible || this.scmViewService.visibleRepositories.length === 1) { return false; } - return this.repositories.elements.some(r => this.tree.hasElement(r) && this.tree.isCollapsible(r)); + return this.scmViewService.visibleRepositories.some(r => this.tree.hasElement(r) && this.tree.isCollapsible(r)); } areAllProvidersCollapsed(): boolean { - if (!this.visible || this.repositories.elements.length === 1) { + if (!this.visible || this.scmViewService.visibleRepositories.length === 1) { return false; } - return this.repositories.elements.every(r => this.tree.hasElement(r) && (!this.tree.isCollapsible(r) || this.tree.isCollapsed(r))); + return this.scmViewService.visibleRepositories.every(r => this.tree.hasElement(r) && (!this.tree.isCollapsible(r) || this.tree.isCollapsed(r))); } dispose(): void { this.visibilityDisposables.dispose(); this.disposables.dispose(); - - for (const item of this.items) { - item.disposable.dispose(); - } - - this.items = []; + dispose(this.items.values()); + this.items.clear(); } } @@ -1656,6 +1656,7 @@ export class SCMViewPane extends ViewPane { constructor( options: IViewPaneOptions, @ISCMService private scmService: ISCMService, + @ISCMViewService private scmViewService: ISCMViewService, @IKeybindingService protected keybindingService: IKeybindingService, @IThemeService protected themeService: IThemeService, @IContextMenuService protected contextMenuService: IContextMenuService, @@ -1696,14 +1697,11 @@ export class SCMViewPane extends ViewPane { this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.providerCountBadge'))(updateProviderCountVisibility)); updateProviderCountVisibility(); - const repositories = new SimpleSequence(this.scmService.repositories, this.scmService.onDidAddRepository, this.scmService.onDidRemoveRepository); - this._register(repositories); - - this.menus = this.instantiationService.createInstance(SCMMenus, repositories); + this.menus = this.instantiationService.createInstance(SCMMenus); this._register(this.menus); this._register(this.menus.titleMenu.onDidChangeTitle(this.updateActions, this)); - this._register(repositories.onDidSplice(() => this.updateActions())); + this._register(this.scmViewService.onDidChangeVisibleRepositories(() => this.updateActions())); this.inputRenderer = this.instantiationService.createInstance(InputRenderer, this.layoutCache, (input, height) => this.tree.updateElementHeight(input, height)); const delegate = new ProviderListDelegate(this.inputRenderer); @@ -1762,7 +1760,7 @@ export class SCMViewPane extends ViewPane { viewMode = storageMode; } - this.viewModel = this.instantiationService.createInstance(ViewModel, repositories, this.tree, this.menus, this.inputRenderer, viewMode, ViewModelSortKey.Path); + this.viewModel = this.instantiationService.createInstance(ViewModel, this.tree, this.menus, this.inputRenderer, viewMode, ViewModelSortKey.Path); this._register(this.viewModel); addClass(this.listContainer, 'file-icon-themable-tree'); @@ -1829,7 +1827,7 @@ export class SCMViewPane extends ViewPane { return result; } - if (this.viewModel.repositories.elements.length < 2) { + if (this.scmViewService.visibleRepositories.length < 2) { return [...result, ...this.viewModel.getViewActions()]; } diff --git a/src/vs/workbench/contrib/scm/common/scm.ts b/src/vs/workbench/contrib/scm/common/scm.ts index 7ee09f52d8a..ccad186a67e 100644 --- a/src/vs/workbench/contrib/scm/common/scm.ts +++ b/src/vs/workbench/contrib/scm/common/scm.ts @@ -113,3 +113,17 @@ export interface ISCMService { registerSCMProvider(provider: ISCMProvider): ISCMRepository; } + +export const ISCMViewService = createDecorator('scmView'); + +export interface ISCMViewVisibleRepositoryChangeEvent { + readonly added: Iterable; + readonly removed: Iterable; +} + +export interface ISCMViewService { + readonly _serviceBrand: undefined; + + visibleRepositories: ISCMRepository[]; + readonly onDidChangeVisibleRepositories: Event; +} diff --git a/src/vs/workbench/contrib/scm/common/scmViewService.ts b/src/vs/workbench/contrib/scm/common/scmViewService.ts new file mode 100644 index 00000000000..2ae341930d8 --- /dev/null +++ b/src/vs/workbench/contrib/scm/common/scmViewService.ts @@ -0,0 +1,82 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { Emitter } from 'vs/base/common/event'; +import { ISCMViewService, ISCMRepository, ISCMService, ISCMViewVisibleRepositoryChangeEvent } from './scm'; +import { Iterable } from 'vs/base/common/iterator'; + +export class SCMViewService implements ISCMViewService { + + declare readonly _serviceBrand: undefined; + + private disposables = new DisposableStore(); + + private _visibleRepositoriesSet = new Set(); + private _visibleRepositories: ISCMRepository[] = []; + + get visibleRepositories(): ISCMRepository[] { + return this._visibleRepositories; + } + + set visibleRepositories(visibleRepositories: ISCMRepository[]) { + const set = new Set(visibleRepositories); + const added = new Set(); + const removed = new Set(); + + for (const repository of visibleRepositories) { + if (!this._visibleRepositoriesSet.has(repository)) { + added.add(repository); + } + } + + for (const repository of this._visibleRepositories) { + if (!set.has(repository)) { + removed.add(repository); + } + } + + if (added.size === 0 && removed.size === 0) { + return; + } + + this._visibleRepositories = visibleRepositories; + this._visibleRepositoriesSet = set; + this._onDidChangeVisibleRepositories.fire({ added, removed }); + } + + private _onDidChangeVisibleRepositories = new Emitter(); + readonly onDidChangeVisibleRepositories = this._onDidChangeVisibleRepositories.event; + + constructor(@ISCMService scmService: ISCMService) { + scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables); + scmService.onDidRemoveRepository(this.onDidRemoveRepository, this, this.disposables); + + for (const repository of scmService.repositories) { + this.onDidAddRepository(repository); + } + } + + private onDidAddRepository(repository: ISCMRepository): void { + this._visibleRepositories.push(repository); + this._visibleRepositoriesSet.add(repository); + this._onDidChangeVisibleRepositories.fire({ added: [repository], removed: Iterable.empty() }); + } + + private onDidRemoveRepository(repository: ISCMRepository): void { + const index = this._visibleRepositories.indexOf(repository); + + if (index > -1) { + this._visibleRepositories.splice(index, 1); + this._visibleRepositoriesSet.delete(repository); + this._onDidChangeVisibleRepositories.fire({ added: Iterable.empty(), removed: [repository] }); + } + } + + dispose(): void { + this.disposables.dispose(); + this._onDidChangeVisibleRepositories.dispose(); + } +} From cf4e4c17544486f6442cb91eeb8f1acfc36d1339 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 10 Aug 2020 11:20:34 +0200 Subject: [PATCH 043/736] Add JUnit reporter for unit tests (#104354) --- test/unit/browser/index.js | 61 +++++++++++++++++++++++-------------- test/unit/electron/index.js | 15 +++++---- 2 files changed, 45 insertions(+), 31 deletions(-) diff --git a/test/unit/browser/index.js b/test/unit/browser/index.js index 926d96f6352..a2640613c6e 100644 --- a/test/unit/browser/index.js +++ b/test/unit/browser/index.js @@ -10,6 +10,7 @@ const glob = require('glob'); const fs = require('fs'); const events = require('events'); const mocha = require('mocha'); +const MochaJUnitReporter = require('mocha-junit-reporter'); const url = require('url'); const minimatch = require('minimatch'); const playwright = require('playwright'); @@ -37,30 +38,44 @@ if (argv.help) { } const withReporter = (function () { - const reporterPath = path.join(path.dirname(require.resolve('mocha')), 'lib', 'reporters', argv.reporter); - let ctor; - - try { - ctor = require(reporterPath); - } catch (err) { - try { - ctor = require(argv.reporter); - } catch (err) { - ctor = process.platform === 'win32' ? mocha.reporters.List : mocha.reporters.Spec; - console.warn(`could not load reporter: ${argv.reporter}, using ${ctor.name}`); + if (argv.tfs) { + { + return (browserType, runner) => { + new mocha.reporters.Spec(runner); + new MochaJUnitReporter(runner, { + reporterOptions: { + testsuitesTitle: `${argv.tfs} ${process.platform}`, + mochaFile: process.env.BUILD_ARTIFACTSTAGINGDIRECTORY ? path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${browserType}-${argv.tfs.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`) : undefined + } + }); + } } + } else { + const reporterPath = path.join(path.dirname(require.resolve('mocha')), 'lib', 'reporters', argv.reporter); + let ctor; + + try { + ctor = require(reporterPath); + } catch (err) { + try { + ctor = require(argv.reporter); + } catch (err) { + ctor = process.platform === 'win32' ? mocha.reporters.List : mocha.reporters.Spec; + console.warn(`could not load reporter: ${argv.reporter}, using ${ctor.name}`); + } + } + + function parseReporterOption(value) { + let r = /^([^=]+)=(.*)$/.exec(value); + return r ? { [r[1]]: r[2] } : {}; + } + + let reporterOptions = argv['reporter-options']; + reporterOptions = typeof reporterOptions === 'string' ? [reporterOptions] : reporterOptions; + reporterOptions = reporterOptions.reduce((r, o) => Object.assign(r, parseReporterOption(o)), {}); + + return (_, runner) => new ctor(runner, { reporterOptions }) } - - function parseReporterOption(value) { - let r = /^([^=]+)=(.*)$/.exec(value); - return r ? { [r[1]]: r[2] } : {}; - } - - let reporterOptions = argv['reporter-options']; - reporterOptions = typeof reporterOptions === 'string' ? [reporterOptions] : reporterOptions; - reporterOptions = reporterOptions.reduce((r, o) => Object.assign(r, parseReporterOption(o)), {}); - - return (runner) => new ctor(runner, { reporterOptions }) })() const outdir = argv.build ? 'out-build' : 'out'; @@ -137,7 +152,7 @@ async function runTestsInBrowser(testModules, browserType) { console[msg.type()](msg.text(), await Promise.all(msg.args().map(async arg => await arg.jsonValue()))); }); - withReporter(new EchoRunner(emitter, browserType.toUpperCase())); + withReporter(browserType, new EchoRunner(emitter, browserType.toUpperCase())); // collection failures for console printing const fails = []; diff --git a/test/unit/electron/index.js b/test/unit/electron/index.js index dcd00923c9b..4b5ce8cb9e9 100644 --- a/test/unit/electron/index.js +++ b/test/unit/electron/index.js @@ -9,7 +9,7 @@ const { join } = require('path'); const path = require('path'); const mocha = require('mocha'); const events = require('events'); -// const MochaJUnitReporter = require('mocha-junit-reporter'); +const MochaJUnitReporter = require('mocha-junit-reporter'); const url = require('url'); const defaultReporterName = process.platform === 'win32' ? 'list' : 'spec'; @@ -136,13 +136,12 @@ app.on('ready', () => { if (argv.tfs) { new mocha.reporters.Spec(runner); - // TODO@deepak the mocha Junit reporter seems to cause a hang when running with Electron 6 inside docker container - // new MochaJUnitReporter(runner, { - // reporterOptions: { - // testsuitesTitle: `${argv.tfs} ${process.platform}`, - // mochaFile: process.env.BUILD_ARTIFACTSTAGINGDIRECTORY ? path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${argv.tfs.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`) : undefined - // } - // }); + new MochaJUnitReporter(runner, { + reporterOptions: { + testsuitesTitle: `${argv.tfs} ${process.platform}`, + mochaFile: process.env.BUILD_ARTIFACTSTAGINGDIRECTORY ? path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${argv.tfs.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`) : undefined + } + }); } else { const reporterPath = path.join(path.dirname(require.resolve('mocha')), 'lib', 'reporters', argv.reporter); let Reporter; From daee9ca2cea5895f2c62f32b5a905781d14dd7ce Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 10 Aug 2020 12:32:08 +0200 Subject: [PATCH 044/736] Azure DevOps pipeline improvements (#103782) * Refactored to use multi-stage pipeline --- .../darwin/continuous-build-darwin.yml | 2 +- .../darwin/product-build-darwin.yml | 9 +- .../linux/continuous-build-linux.yml | 2 +- .../linux/product-build-linux.yml | 9 +- build/azure-pipelines/product-build.yml | 278 ++++++++---------- .../win32/continuous-build-win32.yml | 2 +- .../win32/product-build-win32.yml | 9 +- 7 files changed, 151 insertions(+), 160 deletions(-) diff --git a/build/azure-pipelines/darwin/continuous-build-darwin.yml b/build/azure-pipelines/darwin/continuous-build-darwin.yml index 5785de63367..447e18e7cb5 100644 --- a/build/azure-pipelines/darwin/continuous-build-darwin.yml +++ b/build/azure-pipelines/darwin/continuous-build-darwin.yml @@ -50,7 +50,7 @@ steps: displayName: Run Unit Tests (Electron) - script: | - yarn test-browser --browser chromium --browser webkit --browser firefox + yarn test-browser --browser chromium --browser webkit --browser firefox --tfs "Browser Unit Tests" displayName: Run Unit Tests (Browser) - script: | diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index ea286ef1418..e231ca3d4f2 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -101,7 +101,7 @@ steps: - script: | set -e - yarn test-browser --build --browser chromium --browser webkit --browser firefox + yarn test-browser --build --browser chromium --browser webkit --browser firefox --tfs "Browser Unit Tests" displayName: Run unit tests (Browser) condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) @@ -160,6 +160,13 @@ steps: continueOnError: true condition: failed() +- task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: '*-results.xml' + searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' + condition: succeededOrFailed() + - script: | set -e security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain diff --git a/build/azure-pipelines/linux/continuous-build-linux.yml b/build/azure-pipelines/linux/continuous-build-linux.yml index fdd4c305cda..3e239caad54 100644 --- a/build/azure-pipelines/linux/continuous-build-linux.yml +++ b/build/azure-pipelines/linux/continuous-build-linux.yml @@ -63,7 +63,7 @@ steps: displayName: Run Unit Tests (Electron) - script: | - DISPLAY=:10 yarn test-browser --browser chromium + DISPLAY=:10 yarn test-browser --browser chromium --tfs "Browser Unit Tests" displayName: Run Unit Tests (Browser) - script: | diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 5d7bccf467f..b164e915d7e 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -106,7 +106,7 @@ steps: - script: | set -e - DISPLAY=:10 yarn test-browser --build --browser chromium + DISPLAY=:10 yarn test-browser --build --browser chromium --tfs "Browser Unit Tests" displayName: Run unit tests (Browser) condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) @@ -148,6 +148,13 @@ steps: continueOnError: true condition: failed() +- task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: '*-results.xml' + searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' + condition: succeededOrFailed() + - script: | set -e yarn gulp "vscode-linux-x64-build-deb" diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 7b6d2bcbbde..d946d638584 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -1,157 +1,3 @@ -resources: - containers: - - container: vscode-x64 - image: vscodehub.azurecr.io/vscode-linux-build-agent:x64 - endpoint: VSCodeHub - - container: snapcraft - image: snapcore/snapcraft:stable - -jobs: -- job: Compile - pool: - vmImage: 'Ubuntu-16.04' - container: vscode-x64 - steps: - - template: product-compile.yml - -- job: Windows - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_WIN32'], 'true')) - pool: - vmImage: VS2017-Win2016 - variables: - VSCODE_ARCH: x64 - dependsOn: - - Compile - steps: - - template: win32/product-build-win32.yml - -- job: Windows32 - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_WIN32_32BIT'], 'true')) - pool: - vmImage: VS2017-Win2016 - variables: - VSCODE_ARCH: ia32 - dependsOn: - - Compile - steps: - - template: win32/product-build-win32.yml - -- job: WindowsARM64 - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_WIN32_ARM64'], 'true')) - pool: - vmImage: VS2017-Win2016 - variables: - VSCODE_ARCH: arm64 - dependsOn: - - Compile - steps: - - template: win32/product-build-win32-arm64.yml - -- job: Linux - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX'], 'true')) - pool: - vmImage: 'Ubuntu-16.04' - container: vscode-x64 - dependsOn: - - Compile - steps: - - template: linux/product-build-linux.yml - -- job: LinuxSnap - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX'], 'true')) - pool: - vmImage: 'Ubuntu-16.04' - container: snapcraft - dependsOn: Linux - steps: - - template: linux/snap-build-linux.yml - -- job: LinuxArmhf - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX_ARMHF'], 'true')) - pool: - vmImage: 'Ubuntu-16.04' - variables: - VSCODE_ARCH: armhf - dependsOn: - - Compile - steps: - - template: linux/product-build-linux-multiarch.yml - -- job: LinuxArm64 - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX_ARM64'], 'true')) - pool: - vmImage: 'Ubuntu-16.04' - variables: - VSCODE_ARCH: arm64 - dependsOn: - - Compile - steps: - - template: linux/product-build-linux-multiarch.yml - -- job: LinuxAlpine - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX_ALPINE'], 'true')) - pool: - vmImage: 'Ubuntu-16.04' - variables: - VSCODE_ARCH: alpine - dependsOn: - - Compile - steps: - - template: linux/product-build-linux-multiarch.yml - -- job: LinuxWeb - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_WEB'], 'true')) - pool: - vmImage: 'Ubuntu-16.04' - variables: - VSCODE_ARCH: x64 - dependsOn: - - Compile - steps: - - template: web/product-build-web.yml - -- job: macOS - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_MACOS'], 'true')) - pool: - vmImage: macOS-latest - dependsOn: - - Compile - steps: - - template: darwin/product-build-darwin.yml - -- job: Release - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), or(eq(variables['VSCODE_RELEASE'], 'true'), and(or(eq(variables['VSCODE_QUALITY'], 'insider'), eq(variables['VSCODE_QUALITY'], 'exploration')), eq(variables['Build.Reason'], 'Schedule')))) - pool: - vmImage: 'Ubuntu-16.04' - dependsOn: - - Windows - - Windows32 - - Linux - - LinuxSnap - - LinuxArmhf - - LinuxArm64 - - LinuxAlpine - - macOS - steps: - - template: release.yml - -- job: Mooncake - pool: - vmImage: 'Ubuntu-16.04' - condition: and(succeededOrFailed(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) - dependsOn: - - Windows - - Windows32 - - Linux - - LinuxSnap - - LinuxArmhf - - LinuxArm64 - - LinuxAlpine - - LinuxWeb - - macOS - steps: - - template: sync-mooncake.yml - trigger: none pr: none @@ -161,3 +7,127 @@ schedules: branches: include: - master + +resources: + containers: + - container: vscode-x64 + image: vscodehub.azurecr.io/vscode-linux-build-agent:x64 + endpoint: VSCodeHub + - container: snapcraft + image: snapcore/snapcraft:stable + +stages: +- stage: Compile + jobs: + - job: Compile + pool: + vmImage: 'Ubuntu-16.04' + container: vscode-x64 + steps: + - template: product-compile.yml + +- stage: Windows + dependsOn: + - Compile + pool: + vmImage: VS2017-Win2016 + jobs: + - job: Windows + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_WIN32'], 'true')) + variables: + VSCODE_ARCH: x64 + steps: + - template: win32/product-build-win32.yml + + - job: Windows32 + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_WIN32_32BIT'], 'true')) + variables: + VSCODE_ARCH: ia32 + steps: + - template: win32/product-build-win32.yml + + - job: WindowsARM64 + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_WIN32_ARM64'], 'true')) + variables: + VSCODE_ARCH: arm64 + steps: + - template: win32/product-build-win32-arm64.yml + +- stage: Linux + dependsOn: + - Compile + pool: + vmImage: 'Ubuntu-16.04' + jobs: + - job: Linux + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX'], 'true')) + container: vscode-x64 + steps: + - template: linux/product-build-linux.yml + + - job: LinuxSnap + dependsOn: + - Linux + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX'], 'true')) + container: snapcraft + steps: + - template: linux/snap-build-linux.yml + + - job: LinuxArmhf + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX_ARMHF'], 'true')) + variables: + VSCODE_ARCH: armhf + steps: + - template: linux/product-build-linux-multiarch.yml + + - job: LinuxArm64 + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX_ARM64'], 'true')) + variables: + VSCODE_ARCH: arm64 + steps: + - template: linux/product-build-linux-multiarch.yml + + - job: LinuxAlpine + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX_ALPINE'], 'true')) + variables: + VSCODE_ARCH: alpine + steps: + - template: linux/product-build-linux-multiarch.yml + + - job: LinuxWeb + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_WEB'], 'true')) + variables: + VSCODE_ARCH: x64 + steps: + - template: web/product-build-web.yml + +- stage: macOS + dependsOn: + - Compile + pool: + vmImage: macOS-latest + jobs: + - job: macOS + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_MACOS'], 'true')) + steps: + - template: darwin/product-build-darwin.yml + +- stage: Publish + dependsOn: + - Windows + - Linux + - macOS + pool: + vmImage: 'Ubuntu-16.04' + jobs: + - job: BuildService + displayName: Build Service + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), or(eq(variables['VSCODE_RELEASE'], 'true'), and(or(eq(variables['VSCODE_QUALITY'], 'insider'), eq(variables['VSCODE_QUALITY'], 'exploration')), eq(variables['Build.Reason'], 'Schedule')))) + steps: + - template: release.yml + + - job: BuildServiceMooncake + displayName: Build Service (Mooncake) + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) + steps: + - template: sync-mooncake.yml diff --git a/build/azure-pipelines/win32/continuous-build-win32.yml b/build/azure-pipelines/win32/continuous-build-win32.yml index 026a162f510..1be638a4794 100644 --- a/build/azure-pipelines/win32/continuous-build-win32.yml +++ b/build/azure-pipelines/win32/continuous-build-win32.yml @@ -57,7 +57,7 @@ steps: displayName: Run Unit Tests (Electron) - powershell: | - yarn test-browser --browser chromium --browser firefox + yarn test-browser --browser chromium --browser firefox --tfs "Browser Unit Tests" displayName: Run Unit Tests (Browser) - powershell: | diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index fb4f3052578..1c958ddd188 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -115,7 +115,7 @@ steps: - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - exec { yarn test-browser --build --browser chromium --browser firefox } + exec { yarn test-browser --build --browser chromium --browser firefox --tfs "Browser Unit Tests" } displayName: Run unit tests (Browser) condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) @@ -157,6 +157,13 @@ steps: continueOnError: true condition: failed() +- task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: '*-results.xml' + searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' + condition: succeededOrFailed() + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 inputs: ConnectedServiceName: 'ESRP CodeSign' From 898e349ad5a61ced121ad7b4589f46e3dea6a27d Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 10 Aug 2020 12:35:54 +0200 Subject: [PATCH 045/736] Extract Mooncake into its own stage --- build/azure-pipelines/product-build.yml | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index d946d638584..bf8f099aa38 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -112,6 +112,20 @@ stages: steps: - template: darwin/product-build-darwin.yml +- stage: Mooncake + dependsOn: + - Windows + - Linux + - macOS + pool: + vmImage: 'Ubuntu-16.04' + jobs: + - job: SyncMooncake + displayName: Sync Mooncake + condition: and(succeededOrFailed(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) + steps: + - template: sync-mooncake.yml + - stage: Publish dependsOn: - Windows @@ -125,9 +139,3 @@ stages: condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), or(eq(variables['VSCODE_RELEASE'], 'true'), and(or(eq(variables['VSCODE_QUALITY'], 'insider'), eq(variables['VSCODE_QUALITY'], 'exploration')), eq(variables['Build.Reason'], 'Schedule')))) steps: - template: release.yml - - - job: BuildServiceMooncake - displayName: Build Service (Mooncake) - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) - steps: - - template: sync-mooncake.yml From 4f00cac4346772521349ebb32f94bbba3a4e8ee7 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 10 Aug 2020 11:54:50 +0200 Subject: [PATCH 046/736] update es6 search --- .vscode/searches/es6.code-search | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.vscode/searches/es6.code-search b/.vscode/searches/es6.code-search index 9cf8cf0b264..6ab0d14c5ac 100644 --- a/.vscode/searches/es6.code-search +++ b/.vscode/searches/es6.code-search @@ -34,11 +34,11 @@ src/vs/base/common/arrays.ts: 420 */ 421 export function first(array: ReadonlyArray, fn: (item: T) => boolean, notFoundValue: T): T; - 569 - 570 /** - 571: * @deprecated ES6: use `Array.find` - 572 */ - 573 export function find(arr: ArrayLike, predicate: (value: T, index: number, arr: ArrayLike) => any): T | undefined { + 568 + 569 /** + 570: * @deprecated ES6: use `Array.find` + 571 */ + 572 export function find(arr: ArrayLike, predicate: (value: T, index: number, arr: ArrayLike) => any): T | undefined { src/vs/base/common/objects.ts: 115 From 7551598e6eac7713657e2fa60021ee2e656765fb Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Mon, 10 Aug 2020 13:26:40 +0200 Subject: [PATCH 047/736] node-debug@1.44.9 --- product.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product.json b/product.json index 2b884d18f30..caa4719ce38 100644 --- a/product.json +++ b/product.json @@ -31,7 +31,7 @@ "builtInExtensions": [ { "name": "ms-vscode.node-debug", - "version": "1.44.8", + "version": "1.44.9", "repo": "https://github.com/Microsoft/vscode-node-debug", "metadata": { "id": "b6ded8fb-a0a0-4c1c-acbd-ab2a3bc995a6", From 2a0da2a8408f4476d8b57bfb446bd3d4b4508700 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 10 Aug 2020 14:24:29 +0200 Subject: [PATCH 048/736] Overview ruler should respect breakpoint color fixes #104322 --- .../contrib/debug/browser/breakpointEditorContribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts index 8a98767b7f5..c2bbe5e9a35 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts @@ -32,7 +32,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; import { isSafari } from 'vs/base/browser/browser'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant, themeColorFromId } from 'vs/platform/theme/common/themeService'; import { registerColor } from 'vs/platform/theme/common/colorRegistry'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -87,7 +87,7 @@ function getBreakpointDecorationOptions(model: ITextModel, breakpoint: IBreakpoi let overviewRulerDecoration: IModelDecorationOverviewRulerOptions | null = null; if (showBreakpointsInOverviewRuler) { overviewRulerDecoration = { - color: 'rgb(124, 40, 49)', + color: themeColorFromId(debugIconBreakpointForeground), position: OverviewRulerLane.Left }; } From 0e82f2c782b965fb85add39af0a64c564a7a6ca8 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 10 Aug 2020 14:40:30 +0200 Subject: [PATCH 049/736] Moved conditions to the stage level --- build/azure-pipelines/product-build.yml | 27 ++++++++++++++----------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index bf8f099aa38..1f3a0805086 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -29,25 +29,26 @@ stages: - stage: Windows dependsOn: - Compile + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) pool: vmImage: VS2017-Win2016 jobs: - job: Windows - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_WIN32'], 'true')) + condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32'], 'true')) variables: VSCODE_ARCH: x64 steps: - template: win32/product-build-win32.yml - job: Windows32 - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_WIN32_32BIT'], 'true')) + condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32_32BIT'], 'true')) variables: VSCODE_ARCH: ia32 steps: - template: win32/product-build-win32.yml - job: WindowsARM64 - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_WIN32_ARM64'], 'true')) + condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32_ARM64'], 'true')) variables: VSCODE_ARCH: arm64 steps: @@ -56,11 +57,12 @@ stages: - stage: Linux dependsOn: - Compile + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) pool: vmImage: 'Ubuntu-16.04' jobs: - job: Linux - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX'], 'true')) + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX'], 'true')) container: vscode-x64 steps: - template: linux/product-build-linux.yml @@ -68,34 +70,34 @@ stages: - job: LinuxSnap dependsOn: - Linux - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX'], 'true')) + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX'], 'true')) container: snapcraft steps: - template: linux/snap-build-linux.yml - job: LinuxArmhf - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX_ARMHF'], 'true')) + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ARMHF'], 'true')) variables: VSCODE_ARCH: armhf steps: - template: linux/product-build-linux-multiarch.yml - job: LinuxArm64 - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX_ARM64'], 'true')) + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ARM64'], 'true')) variables: VSCODE_ARCH: arm64 steps: - template: linux/product-build-linux-multiarch.yml - job: LinuxAlpine - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX_ALPINE'], 'true')) + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ALPINE'], 'true')) variables: VSCODE_ARCH: alpine steps: - template: linux/product-build-linux-multiarch.yml - job: LinuxWeb - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_WEB'], 'true')) + condition: and(succeeded(), eq(variables['VSCODE_BUILD_WEB'], 'true')) variables: VSCODE_ARCH: x64 steps: @@ -104,11 +106,12 @@ stages: - stage: macOS dependsOn: - Compile + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) pool: vmImage: macOS-latest jobs: - job: macOS - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_MACOS'], 'true')) + condition: and(succeeded(), eq(variables['VSCODE_BUILD_MACOS'], 'true')) steps: - template: darwin/product-build-darwin.yml @@ -117,12 +120,12 @@ stages: - Windows - Linux - macOS + condition: and(succeededOrFailed(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) pool: vmImage: 'Ubuntu-16.04' jobs: - job: SyncMooncake displayName: Sync Mooncake - condition: and(succeededOrFailed(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) steps: - template: sync-mooncake.yml @@ -131,11 +134,11 @@ stages: - Windows - Linux - macOS + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), or(eq(variables['VSCODE_RELEASE'], 'true'), and(or(eq(variables['VSCODE_QUALITY'], 'insider'), eq(variables['VSCODE_QUALITY'], 'exploration')), eq(variables['Build.Reason'], 'Schedule')))) pool: vmImage: 'Ubuntu-16.04' jobs: - job: BuildService displayName: Build Service - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), or(eq(variables['VSCODE_RELEASE'], 'true'), and(or(eq(variables['VSCODE_QUALITY'], 'insider'), eq(variables['VSCODE_QUALITY'], 'exploration')), eq(variables['Build.Reason'], 'Schedule')))) steps: - template: release.yml From 83b02fa2e3d7cf40ca59bfb3fb15afc905c107f3 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 10 Aug 2020 15:34:10 +0200 Subject: [PATCH 050/736] preLaunchTask should complete before resolveDebugConfigurationWithSubstitutedVariables fixes #95162 --- .../workbench/contrib/debug/browser/debugService.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 7bba378be0c..5b95e4ce9c3 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -412,6 +412,12 @@ export class DebugService implements IDebugService { return false; } + const workspace = launch?.workspace || this.contextService.getWorkspace(); + const taskResult = await this.taskRunner.runTaskAndCheckErrors(workspace, resolvedConfig.preLaunchTask, (msg, actions) => this.showError(msg, actions)); + if (taskResult === TaskRunResult.Failure) { + return false; + } + const cfg = await this.configurationManager.resolveDebugConfigurationWithSubstitutedVariables(launch && launch.workspace ? launch.workspace.uri : undefined, type, resolvedConfig, initCancellationToken.token); if (!cfg) { if (launch && type && cfg === null && !initCancellationToken.token.isCancellationRequested) { // show launch.json only for "config" being "null". @@ -436,12 +442,7 @@ export class DebugService implements IDebugService { return false; } - const workspace = launch?.workspace || this.contextService.getWorkspace(); - const taskResult = await this.taskRunner.runTaskAndCheckErrors(workspace, resolvedConfig.preLaunchTask, (msg, actions) => this.showError(msg, actions)); - if (taskResult === TaskRunResult.Success) { - return this.doCreateSession(sessionId, launch?.workspace, { resolved: resolvedConfig, unresolved: unresolvedConfig }, options); - } - return false; + return this.doCreateSession(sessionId, launch?.workspace, { resolved: resolvedConfig, unresolved: unresolvedConfig }, options); } catch (err) { if (err && err.message) { await this.showError(err.message); From f2719315fdaac0678be924c7f0ba2fca10514584 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 10 Aug 2020 15:42:56 +0200 Subject: [PATCH 051/736] update references view --- product.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product.json b/product.json index caa4719ce38..efbb04d8b55 100644 --- a/product.json +++ b/product.json @@ -61,7 +61,7 @@ }, { "name": "ms-vscode.references-view", - "version": "0.0.61", + "version": "0.0.62", "repo": "https://github.com/Microsoft/vscode-reference-view", "metadata": { "id": "dc489f46-520d-4556-ae85-1f9eab3c412d", From 53d77440f8c0f9bf9e8cb5f2577697757b7417f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 10 Aug 2020 15:54:06 +0200 Subject: [PATCH 052/736] fixes #104299 --- .../contrib/markers/browser/constants.ts | 1 + .../markers/browser/markers.contribution.ts | 15 +++++++++++++++ .../search/browser/search.contribution.ts | 18 ++++++++++++++++++ .../contrib/search/common/constants.ts | 1 + 4 files changed, 35 insertions(+) diff --git a/src/vs/workbench/contrib/markers/browser/constants.ts b/src/vs/workbench/contrib/markers/browser/constants.ts index 6714a443377..963ec0c8130 100644 --- a/src/vs/workbench/contrib/markers/browser/constants.ts +++ b/src/vs/workbench/contrib/markers/browser/constants.ts @@ -17,6 +17,7 @@ export default { MARKERS_VIEW_CLEAR_FILTER_TEXT: 'problems.action.clearFilterText', MARKERS_VIEW_SHOW_MULTILINE_MESSAGE: 'problems.action.showMultilineMessage', MARKERS_VIEW_SHOW_SINGLELINE_MESSAGE: 'problems.action.showSinglelineMessage', + MARKER_OPEN_ACTION_ID: 'problems.action.open', MARKER_OPEN_SIDE_ACTION_ID: 'problems.action.openToSide', MARKER_SHOW_PANEL_ID: 'workbench.action.showErrorsWarnings', MARKER_SHOW_QUICK_FIX: 'problems.action.showQuickFixes', diff --git a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts index 040dcdfcb94..5ded9c8459f 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts @@ -36,6 +36,21 @@ import { Codicon } from 'vs/base/common/codicons'; registerSingleton(IMarkersWorkbenchService, MarkersWorkbenchService, false); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: Constants.MARKER_OPEN_ACTION_ID, + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and(Constants.MarkerFocusContextKey), + primary: KeyCode.Enter, + mac: { + primary: KeyCode.Enter, + secondary: [KeyMod.CtrlCmd | KeyCode.DownArrow] + }, + handler: (accessor, args: any) => { + const markersView = accessor.get(IViewsService).getActiveViewWithId(Constants.MARKERS_VIEW_ID)!; + markersView.openFileAtElement(markersView.getFocusElement(), false, false, true); + } +}); + KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Constants.MARKER_OPEN_SIDE_ACTION_ID, weight: KeybindingWeight.WorkbenchContrib, diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 621dd65d123..b4cfa82062b 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -94,6 +94,24 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: Constants.OpenMatch, + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.FileMatchOrMatchFocusKey), + primary: KeyCode.Enter, + mac: { + primary: KeyCode.Enter, + secondary: [KeyMod.CtrlCmd | KeyCode.DownArrow] + }, + handler: (accessor) => { + const searchView = getSearchView(accessor.get(IViewsService)); + if (searchView) { + const tree: WorkbenchObjectTree = searchView.getControl(); + searchView.open(tree.getFocus()[0], false, false, true); + } + } +}); + KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Constants.OpenMatchToSide, weight: KeybindingWeight.WorkbenchContrib, diff --git a/src/vs/workbench/contrib/search/common/constants.ts b/src/vs/workbench/contrib/search/common/constants.ts index 5ac1fa1a6fd..f1e6975f788 100644 --- a/src/vs/workbench/contrib/search/common/constants.ts +++ b/src/vs/workbench/contrib/search/common/constants.ts @@ -9,6 +9,7 @@ export const FindInFilesActionId = 'workbench.action.findInFiles'; export const FocusActiveEditorCommandId = 'search.action.focusActiveEditor'; export const FocusSearchFromResults = 'search.action.focusSearchFromResults'; +export const OpenMatch = 'search.action.openResult'; export const OpenMatchToSide = 'search.action.openResultToSide'; export const CancelActionId = 'search.action.cancel'; export const RemoveActionId = 'search.action.remove'; From c68d03f5c7073858c61de953f8d5f96f0ddbd94a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 10 Aug 2020 16:33:32 +0200 Subject: [PATCH 053/736] update distro --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 80325e14c5d..46b582a2a37 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.49.0", - "distro": "db6c4e7925a5077a6e61690a057fa6cf139e0e49", + "distro": "d368861bfb088f58e20fd0ebbcaeac0104e81f80", "author": { "name": "Microsoft Corporation" }, @@ -191,4 +191,4 @@ "windows-mutex": "0.3.0", "windows-process-tree": "0.2.4" } -} +} \ No newline at end of file From f40faced57375953694a352221b5fe8741255b1f Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 10 Aug 2020 07:34:29 -0700 Subject: [PATCH 054/736] fixes #104293 --- .../electron-browser/experimentService.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/experiment/electron-browser/experimentService.ts b/src/vs/workbench/services/experiment/electron-browser/experimentService.ts index 966ea7736f8..e13d6b613d2 100644 --- a/src/vs/workbench/services/experiment/electron-browser/experimentService.ts +++ b/src/vs/workbench/services/experiment/electron-browser/experimentService.ts @@ -12,6 +12,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { ITelemetryData } from 'vs/base/common/actions'; import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; const storageKey = 'VSCode.ABExp.FeatureData'; const refetchInterval = 1000 * 60 * 30; // By default it's set up to 30 minutes. @@ -160,13 +161,18 @@ export class ExperimentService implements ITASExperimentService { private tasClient: Promise | undefined; private static MEMENTO_ID = 'experiment.service.memento'; + private get experimentsEnabled(): boolean { + return this.configurationService.getValue('workbench.enableExperiments') === true; + } + constructor( @IProductService private productService: IProductService, @ITelemetryService private telemetryService: ITelemetryService, - @IStorageService private storageService: IStorageService + @IStorageService private storageService: IStorageService, + @IConfigurationService private configurationService: IConfigurationService, ) { - if (this.productService.tasConfig) { + if (this.productService.tasConfig && this.experimentsEnabled) { this.tasClient = this.setupTASClient(); } } @@ -176,6 +182,10 @@ export class ExperimentService implements ITASExperimentService { return undefined; } + if (!this.experimentsEnabled) { + return undefined; + } + return (await this.tasClient).getTreatmentVariable('vscode', name); } From 57e1d1aac79a7d56ed133c10439ff496fb86e9c1 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 10 Aug 2020 07:42:55 -0700 Subject: [PATCH 055/736] no polling --- .../services/experiment/electron-browser/experimentService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/experiment/electron-browser/experimentService.ts b/src/vs/workbench/services/experiment/electron-browser/experimentService.ts index e13d6b613d2..8a5016a8f51 100644 --- a/src/vs/workbench/services/experiment/electron-browser/experimentService.ts +++ b/src/vs/workbench/services/experiment/electron-browser/experimentService.ts @@ -15,7 +15,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; const storageKey = 'VSCode.ABExp.FeatureData'; -const refetchInterval = 1000 * 60 * 30; // By default it's set up to 30 minutes. +const refetchInterval = 0; // no polling class MementoKeyValueStorage implements IKeyValueStorage { constructor(private mementoObj: MementoObject) { } From 2fa705b33d86ec61c95a41d9a9184cbd4d054100 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 10 Aug 2020 07:45:20 -0700 Subject: [PATCH 056/736] duplicate copy action --- src/vs/workbench/services/dialogs/browser/dialogService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/dialogs/browser/dialogService.ts b/src/vs/workbench/services/dialogs/browser/dialogService.ts index 6e3182a696d..1360c248eb7 100644 --- a/src/vs/workbench/services/dialogs/browser/dialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/dialogService.ts @@ -24,7 +24,7 @@ export class DialogService implements IDialogService { declare readonly _serviceBrand: undefined; - private allowableCommands = ['copy', 'cut', 'editor.action.clipboardCopyAction', 'editor.action.clipboardCopyAction']; + private allowableCommands = ['copy', 'cut', 'editor.action.clipboardCopyAction', 'editor.action.clipboardCutAction']; constructor( @ILogService private readonly logService: ILogService, From fc5df39d51d736b5c94d115ca92ee1af9ceb9832 Mon Sep 17 00:00:00 2001 From: Chuang Yu Date: Mon, 10 Aug 2020 23:52:36 +0800 Subject: [PATCH 057/736] use IEditorProgressService --- src/vs/editor/contrib/format/formatActions.ts | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index 229272931b6..414809c99b0 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -25,7 +25,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { onUnexpectedError } from 'vs/base/common/errors'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { Progress, IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; +import { Progress, IEditorProgressService } from 'vs/platform/progress/common/progress'; class FormatOnType implements IEditorContribution { @@ -231,13 +231,10 @@ class FormatDocumentAction extends EditorAction { async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { if (editor.hasModel()) { const instaService = accessor.get(IInstantiationService); - const progressService = accessor.get(IProgressService); - return progressService.withProgress({ - location: ProgressLocation.Window, - title: nls.localize("formatDocument.label.formatting", "Formatting document...") - }, async () => { - return await instaService.invokeFunction(formatDocumentWithSelectedProvider, editor, FormattingMode.Explicit, Progress.None, CancellationToken.None); - }); + const progressService = accessor.get(IEditorProgressService); + return progressService.showWhile( + instaService.invokeFunction(formatDocumentWithSelectedProvider, editor, FormattingMode.Explicit, Progress.None, CancellationToken.None) + ); } } } @@ -274,13 +271,10 @@ class FormatSelectionAction extends EditorAction { range = new Range(range.startLineNumber, 1, range.startLineNumber, model.getLineMaxColumn(range.startLineNumber)); } - const progressService = accessor.get(IProgressService); - return progressService.withProgress({ - location: ProgressLocation.Window, - title: nls.localize("formatSelection.label.formatting", "Formatting selection...") - }, async () => { - return await instaService.invokeFunction(formatDocumentRangeWithSelectedProvider, editor, range, FormattingMode.Explicit, CancellationToken.None); - }); + const progressService = accessor.get(IEditorProgressService); + return progressService.showWhile( + instaService.invokeFunction(formatDocumentRangeWithSelectedProvider, editor, range, FormattingMode.Explicit, CancellationToken.None) + ); } } From 5da15b058fac166a84eb3a3add6193903683bb20 Mon Sep 17 00:00:00 2001 From: Chuang Yu Date: Mon, 10 Aug 2020 23:57:28 +0800 Subject: [PATCH 058/736] await for promise finishing --- src/vs/editor/contrib/format/formatActions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index 414809c99b0..0282114d516 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -232,7 +232,7 @@ class FormatDocumentAction extends EditorAction { if (editor.hasModel()) { const instaService = accessor.get(IInstantiationService); const progressService = accessor.get(IEditorProgressService); - return progressService.showWhile( + await progressService.showWhile( instaService.invokeFunction(formatDocumentWithSelectedProvider, editor, FormattingMode.Explicit, Progress.None, CancellationToken.None) ); } @@ -272,7 +272,7 @@ class FormatSelectionAction extends EditorAction { } const progressService = accessor.get(IEditorProgressService); - return progressService.showWhile( + await progressService.showWhile( instaService.invokeFunction(formatDocumentRangeWithSelectedProvider, editor, range, FormattingMode.Explicit, CancellationToken.None) ); } From 584170e7374c1b26d9e42a81d1a55dd9ae4639e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 10 Aug 2020 18:00:28 +0200 Subject: [PATCH 059/736] extract RepositoryRenderer --- .../scm/browser/scmRepositoryRenderer.ts | 127 ++++++++++++++++ .../contrib/scm/browser/scmViewPane.ts | 143 +----------------- 2 files changed, 131 insertions(+), 139 deletions(-) create mode 100644 src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts diff --git a/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts b/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts new file mode 100644 index 00000000000..4d7514db007 --- /dev/null +++ b/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts @@ -0,0 +1,127 @@ +/*--------------------------------------------------------------------------------------------- + * 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/scm'; +import { basename } from 'vs/base/common/resources'; +import { IDisposable, Disposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle'; +import { append, $, addClass, toggleClass } from 'vs/base/browser/dom'; +import { ISCMRepository } from 'vs/workbench/contrib/scm/common/scm'; +import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IAction, IActionViewItemProvider } from 'vs/base/common/actions'; +import { SCMMenus } from './menus'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { connectPrimaryMenu, StatusBarAction } from './util'; +import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; +import { ITreeNode } from 'vs/base/browser/ui/tree/tree'; +import { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree'; +import { FuzzyScore } from 'vs/base/common/filters'; +import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; + +interface RepositoryTemplate { + readonly label: HTMLElement; + readonly name: HTMLElement; + readonly description: HTMLElement; + readonly countContainer: HTMLElement; + readonly count: CountBadge; + readonly toolBar: ToolBar; + disposable: IDisposable; + readonly templateDisposable: IDisposable; +} + +export class RepositoryRenderer implements ICompressibleTreeRenderer { + + static readonly TEMPLATE_ID = 'repository'; + get templateId(): string { return RepositoryRenderer.TEMPLATE_ID; } + + constructor( + private actionViewItemProvider: IActionViewItemProvider, + private menus: SCMMenus, + @ICommandService private commandService: ICommandService, + @IContextMenuService private contextMenuService: IContextMenuService, + @IThemeService private themeService: IThemeService + ) { } + + renderTemplate(container: HTMLElement): RepositoryTemplate { + // hack + addClass(container.parentElement!.parentElement!.querySelector('.monaco-tl-twistie')! as HTMLElement, 'force-twistie'); + + const provider = append(container, $('.scm-provider')); + const label = append(provider, $('.label')); + const name = append(label, $('span.name')); + const description = append(label, $('span.description')); + const actions = append(provider, $('.actions')); + const toolBar = new ToolBar(actions, this.contextMenuService, { actionViewItemProvider: this.actionViewItemProvider }); + const countContainer = append(provider, $('.count')); + const count = new CountBadge(countContainer); + const badgeStyler = attachBadgeStyler(count, this.themeService); + const visibilityDisposable = toolBar.onDidChangeDropdownVisibility(e => toggleClass(provider, 'active', e)); + + const disposable = Disposable.None; + const templateDisposable = combinedDisposable(visibilityDisposable, toolBar, badgeStyler); + + return { label, name, description, countContainer, count, toolBar, disposable, templateDisposable }; + } + + renderElement(node: ITreeNode, index: number, templateData: RepositoryTemplate, height: number | undefined): void { + templateData.disposable.dispose(); + + const disposables = new DisposableStore(); + const repository = node.element; + + if (repository.provider.rootUri) { + templateData.label.title = `${repository.provider.label}: ${repository.provider.rootUri.fsPath}`; + templateData.name.textContent = basename(repository.provider.rootUri); + templateData.description.textContent = repository.provider.label; + } else { + templateData.label.title = repository.provider.label; + templateData.name.textContent = repository.provider.label; + templateData.description.textContent = ''; + } + + let statusPrimaryActions: IAction[] = []; + let menuPrimaryActions: IAction[] = []; + let menuSecondaryActions: IAction[] = []; + const updateToolbar = () => { + templateData.toolBar.setActions([...statusPrimaryActions, ...menuPrimaryActions], menuSecondaryActions); + }; + + const onDidChangeProvider = () => { + const commands = repository.provider.statusBarCommands || []; + statusPrimaryActions = commands.map(c => new StatusBarAction(c, this.commandService)); + updateToolbar(); + + const count = repository.provider.count || 0; + templateData.countContainer.setAttribute('data-count', String(count)); + templateData.count.setCount(count); + }; + disposables.add(repository.provider.onDidChange(onDidChangeProvider, null)); + onDidChangeProvider(); + + const menus = this.menus.getRepositoryMenus(repository.provider); + disposables.add(connectPrimaryMenu(menus.titleMenu.menu, (primary, secondary) => { + menuPrimaryActions = primary; + menuSecondaryActions = secondary; + updateToolbar(); + })); + templateData.toolBar.context = repository.provider; + + templateData.disposable = disposables; + } + + renderCompressedElements(): void { + throw new Error('Should never happen since node is incompressible'); + } + + disposeElement(group: ITreeNode, index: number, template: RepositoryTemplate): void { + template.disposable.dispose(); + } + + disposeTemplate(templateData: RepositoryTemplate): void { + templateData.disposable.dispose(); + templateData.templateDisposable.dispose(); + } +} diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 784ab9c1500..a7f1d12a3ad 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -24,7 +24,7 @@ import { IAction, IActionViewItem, ActionRunner, Action, RadioGroup, Separator, import { SCMMenus, SCMTitleMenu } from './menus'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { IThemeService, LIGHT, registerThemingParticipant, IFileIconTheme } from 'vs/platform/theme/common/themeService'; -import { isSCMResource, isSCMResourceGroup, connectPrimaryMenuToInlineActionBar, isSCMRepository, isSCMInput, connectPrimaryMenu, collectContextMenuActions } from './util'; +import { isSCMResource, isSCMResourceGroup, connectPrimaryMenuToInlineActionBar, isSCMRepository, isSCMInput, collectContextMenuActions, StatusBarAction, StatusBarActionViewItem } from './util'; import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; import { WorkbenchCompressibleObjectTree, IOpenEvent } from 'vs/platform/list/browser/listService'; import { IConfigurationService, ConfigurationTarget, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; @@ -56,7 +56,7 @@ import { MenuPreventer } from 'vs/workbench/contrib/codeEditor/browser/menuPreve import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard'; import { ContextMenuController } from 'vs/editor/contrib/contextmenu/contextmenu'; import * as platform from 'vs/base/common/platform'; -import { escape, compare, format } from 'vs/base/common/strings'; +import { compare, format } from 'vs/base/common/strings'; import { inputPlaceholderForeground, inputValidationInfoBorder, inputValidationWarningBorder, inputValidationErrorBorder, inputValidationInfoBackground, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningForeground, inputValidationErrorBackground, inputValidationErrorForeground, inputBackground, inputForeground, inputBorder, focusBorder, registerColor, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { SuggestController } from 'vs/editor/contrib/suggest/suggestController'; import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; @@ -72,11 +72,9 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { ILabelService } from 'vs/platform/label/common/label'; import { KeyCode } from 'vs/base/common/keyCodes'; import { DEFAULT_FONT_FAMILY } from 'vs/workbench/browser/style'; -import { Command } from 'vs/editor/common/modes'; -import { renderCodicons, Codicon } from 'vs/base/common/codicons'; -import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; +import { Codicon } from 'vs/base/common/codicons'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; -import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { RepositoryRenderer } from 'vs/workbench/contrib/scm/browser/scmRepositoryRenderer'; type TreeElement = ISCMRepository | ISCMInput | ISCMResourceGroup | IResourceNode | ISCMResource; @@ -113,145 +111,12 @@ function splitMatches(uri: URI, filterData: FuzzyScore | undefined): [IMatch[] | return [matches, descriptionMatches]; } -class StatusBarAction extends Action { - - constructor( - private command: Command, - private commandService: ICommandService - ) { - super(`statusbaraction{${command.id}}`, command.title, '', true); - this.tooltip = command.tooltip || ''; - } - - run(): Promise { - return this.commandService.executeCommand(this.command.id, ...(this.command.arguments || [])); - } -} - -class StatusBarActionViewItem extends ActionViewItem { - - constructor(action: StatusBarAction) { - super(null, action, {}); - } - - updateLabel(): void { - if (this.options.label && this.label) { - this.label.innerHTML = renderCodicons(escape(this.getAction().label)); - } - } -} - interface ISCMLayout { height: number | undefined; width: number | undefined; readonly onDidChange: Event; } -interface RepositoryTemplate { - readonly label: HTMLElement; - readonly name: HTMLElement; - readonly description: HTMLElement; - readonly countContainer: HTMLElement; - readonly count: CountBadge; - readonly toolBar: ToolBar; - disposable: IDisposable; - readonly templateDisposable: IDisposable; -} - -class RepositoryRenderer implements ICompressibleTreeRenderer { - - static readonly TEMPLATE_ID = 'repository'; - get templateId(): string { return RepositoryRenderer.TEMPLATE_ID; } - - constructor( - private actionViewItemProvider: IActionViewItemProvider, - private menus: SCMMenus, - @ICommandService private commandService: ICommandService, - @IContextMenuService private contextMenuService: IContextMenuService, - @IThemeService private themeService: IThemeService - ) { } - - renderTemplate(container: HTMLElement): RepositoryTemplate { - // hack - addClass(container.parentElement!.parentElement!.querySelector('.monaco-tl-twistie')! as HTMLElement, 'force-twistie'); - - const provider = append(container, $('.scm-provider')); - const label = append(provider, $('.label')); - const name = append(label, $('span.name')); - const description = append(label, $('span.description')); - const actions = append(provider, $('.actions')); - const toolBar = new ToolBar(actions, this.contextMenuService, { actionViewItemProvider: this.actionViewItemProvider }); - const countContainer = append(provider, $('.count')); - const count = new CountBadge(countContainer); - const badgeStyler = attachBadgeStyler(count, this.themeService); - const visibilityDisposable = toolBar.onDidChangeDropdownVisibility(e => toggleClass(provider, 'active', e)); - - const disposable = Disposable.None; - const templateDisposable = combinedDisposable(visibilityDisposable, toolBar, badgeStyler); - - return { label, name, description, countContainer, count, toolBar, disposable, templateDisposable }; - } - - renderElement(node: ITreeNode, index: number, templateData: RepositoryTemplate, height: number | undefined): void { - templateData.disposable.dispose(); - - const disposables = new DisposableStore(); - const repository = node.element; - - if (repository.provider.rootUri) { - templateData.label.title = `${repository.provider.label}: ${repository.provider.rootUri.fsPath}`; - templateData.name.textContent = basename(repository.provider.rootUri); - templateData.description.textContent = repository.provider.label; - } else { - templateData.label.title = repository.provider.label; - templateData.name.textContent = repository.provider.label; - templateData.description.textContent = ''; - } - - let statusPrimaryActions: IAction[] = []; - let menuPrimaryActions: IAction[] = []; - let menuSecondaryActions: IAction[] = []; - const updateToolbar = () => { - templateData.toolBar.setActions([...statusPrimaryActions, ...menuPrimaryActions], menuSecondaryActions); - }; - - const onDidChangeProvider = () => { - const commands = repository.provider.statusBarCommands || []; - statusPrimaryActions = commands.map(c => new StatusBarAction(c, this.commandService)); - updateToolbar(); - - const count = repository.provider.count || 0; - templateData.countContainer.setAttribute('data-count', String(count)); - templateData.count.setCount(count); - }; - disposables.add(repository.provider.onDidChange(onDidChangeProvider, null)); - onDidChangeProvider(); - - const menus = this.menus.getRepositoryMenus(repository.provider); - disposables.add(connectPrimaryMenu(menus.titleMenu.menu, (primary, secondary) => { - menuPrimaryActions = primary; - menuSecondaryActions = secondary; - updateToolbar(); - })); - templateData.toolBar.context = repository.provider; - - templateData.disposable = disposables; - } - - renderCompressedElements(): void { - throw new Error('Should never happen since node is incompressible'); - } - - disposeElement(group: ITreeNode, index: number, template: RepositoryTemplate): void { - template.disposable.dispose(); - } - - disposeTemplate(templateData: RepositoryTemplate): void { - templateData.disposable.dispose(); - templateData.templateDisposable.dispose(); - } -} - interface InputTemplate { readonly inputWidget: SCMInputWidget; disposable: IDisposable; From 6458962368b88fa4b4c71716fd0b149fb38e812c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 10 Aug 2020 18:28:58 +0200 Subject: [PATCH 060/736] update integration tests --- extensions/vscode-notebook-tests/src/notebook.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/vscode-notebook-tests/src/notebook.test.ts b/extensions/vscode-notebook-tests/src/notebook.test.ts index 51e5d6ac0ce..c09d171fc6d 100644 --- a/extensions/vscode-notebook-tests/src/notebook.test.ts +++ b/extensions/vscode-notebook-tests/src/notebook.test.ts @@ -132,7 +132,7 @@ suite('Notebook API tests', () => { }); test('notebook open/close, all cell-documents are ready', async function () { - const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb')); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); const p = getEventOncePromise(vscode.notebook.onDidOpenNotebookDocument).then(notebook => { for (let cell of notebook.cells) { @@ -151,7 +151,7 @@ suite('Notebook API tests', () => { }); test('notebook open/close, notebook ready when cell-document open event is fired', async function () { - const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb')); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); let didHappen = false; const p = getEventOncePromise(vscode.workspace.onDidOpenTextDocument).then(doc => { if (doc.uri.scheme !== 'vscode-notebook-cell') { From dbdb5bdac6aeeffd23e69bb32b92e1f2bc692ee0 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 10 Aug 2020 10:09:56 -0700 Subject: [PATCH 061/736] drop below folded markdown cell. --- .../browser/view/renderers/cellRenderer.ts | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index a4bc32bcf2b..88f675c66ea 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -750,19 +750,41 @@ export class CellDragAndDropController extends Disposable { } private async moveCells(draggedCells: ICellViewModel[], ontoCell: ICellViewModel, direction: 'above' | 'below') { + const viewModel = this.notebookEditor.viewModel; + + if (!viewModel) { + return; + } + this.notebookEditor.textModel!.pushStackElement('Move Cells'); if (direction === 'above') { for (let i = 0; i < draggedCells.length; i++) { - const relativeToIndex = this.notebookEditor!.viewModel!.getCellIndex(ontoCell); - const newIdx = relativeToIndex; - - await this.notebookEditor.moveCellToIdx(draggedCells[i], newIdx); + await this.notebookEditor.moveCellToIdx(draggedCells[i], viewModel.getCellIndex(ontoCell)); } } else { - for (let i = draggedCells.length - 1; i >= 0; i--) { - const relativeToIndex = this.notebookEditor!.viewModel!.getCellIndex(ontoCell); - const newIdx = relativeToIndex + 1; - await this.notebookEditor.moveCellToIdx(draggedCells[i], newIdx); + const relativeToIndex = viewModel.getCellIndex(ontoCell); + const newIdx = viewModel.getNextVisibleCellIndex(relativeToIndex); + + if (relativeToIndex + 1 === newIdx) { + // no folding + for (let i = draggedCells.length - 1; i >= 0; i--) { + const newIdx = viewModel.getCellIndex(ontoCell) + 1; + await this.notebookEditor.moveCellToIdx(draggedCells[i], newIdx); + } + } else { + // there is folding + if (newIdx === viewModel.length) { + // insert to the end + for (let i = 0; i < draggedCells.length; i++) { + await this.notebookEditor.moveCellToIdx(draggedCells[i], this.notebookEditor.viewModel!.length); + } + } else { + ontoCell = viewModel.viewCells[newIdx]; + // drop above ontoCell + for (let i = 0; i < draggedCells.length; i++) { + await this.notebookEditor.moveCellToIdx(draggedCells[i], viewModel.getCellIndex(ontoCell)); + } + } } } this.notebookEditor.textModel!.pushStackElement('Move Cells'); From 6ad637946b135cea627adfa4a0da96459526e44b Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 10 Aug 2020 10:07:14 -0700 Subject: [PATCH 062/736] Finalize terminal link providers Fixes #91290 --- src/vs/vscode.d.ts | 67 +++++++++++++++++++ src/vs/vscode.proposed.d.ts | 58 ---------------- .../workbench/api/common/extHost.api.impl.ts | 1 - 3 files changed, 67 insertions(+), 59 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index ef711dc3855..1a3efbc86da 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -5420,6 +5420,66 @@ declare module 'vscode' { dispose(): void; } + /** + * Provides information on a line in a terminal in order to provide links for it. + */ + export interface TerminalLinkContext { + /** + * This is the text from the unwrapped line in the terminal. + */ + line: string; + + /** + * The terminal the link belongs to. + */ + terminal: Terminal; + } + + /** + * A provider that enables detection and handling of links within terminals. + */ + export interface TerminalLinkProvider { + /** + * Provide terminal links for the given context. Note that this can be called multiple times + * even before previous calls resolve, make sure to not share global objects (eg. `RegExp`) + * that could have problems when asynchronous usage may overlap. + * @param context Information about what links are being provided for. + * @param token A cancellation token. + * @return A list of terminal links for the given line. + */ + provideTerminalLinks(context: TerminalLinkContext, token: CancellationToken): ProviderResult + + /** + * Handle an activated terminal link. + * @param link The link to handle. + */ + handleTerminalLink(link: T): ProviderResult; + } + + /** + * A link on a terminal line. + */ + export interface TerminalLink { + /** + * The start index of the link on [TerminalLinkContext.line](#TerminalLinkContext.line]. + */ + startIndex: number; + + /** + * The length of the link on [TerminalLinkContext.line](#TerminalLinkContext.line] + */ + length: number; + + /** + * The tooltip text when you hover over this link. + * + * If a tooltip is provided, is will be displayed in a string that includes instructions on + * how to trigger the link, such as `{0} (ctrl + click)`. The specific instructions vary + * depending on OS, user settings, and localization. + */ + tooltip?: string; + } + /** * In a remote window the extension kind describes if an extension * runs where the UI (window) runs or if an extension runs remotely. @@ -8184,6 +8244,13 @@ declare module 'vscode' { readonly supportsMultipleEditorsPerDocument?: boolean; }): Disposable; + /** + * Register provider that enables the detection and handling of links within the terminal. + * @param provider The provider that provides the terminal links. + * @return Disposable that unregisters the provider. + */ + export function registerTerminalLinkProvider(provider: TerminalLinkProvider): Disposable; + /** * The currently active color theme as configured in the settings. The active * theme can be changed via the `workbench.colorTheme` setting. diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index e971059842b..5c43806131f 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -956,64 +956,6 @@ declare module 'vscode' { //#endregion - //#region Terminal link provider https://github.com/microsoft/vscode/issues/91606 - - export namespace window { - export function registerTerminalLinkProvider(provider: TerminalLinkProvider): Disposable; - } - - export interface TerminalLinkContext { - /** - * This is the text from the unwrapped line in the terminal. - */ - line: string; - - /** - * The terminal the link belongs to. - */ - terminal: Terminal; - } - - export interface TerminalLinkProvider { - /** - * Provide terminal links for the given context. Note that this can be called multiple times - * even before previous calls resolve, make sure to not share global objects (eg. `RegExp`) - * that could have problems when asynchronous usage may overlap. - * @param context Information about what links are being provided for. - * @param token A cancellation token. - * @return A list of terminal links for the given line. - */ - provideTerminalLinks(context: TerminalLinkContext, token: CancellationToken): ProviderResult - - /** - * Handle an activated terminal link. - */ - handleTerminalLink(link: T): ProviderResult; - } - - export interface TerminalLink { - /** - * The start index of the link on [TerminalLinkContext.line](#TerminalLinkContext.line]. - */ - startIndex: number; - - /** - * The length of the link on [TerminalLinkContext.line](#TerminalLinkContext.line] - */ - length: number; - - /** - * The tooltip text when you hover over this link. - * - * If a tooltip is provided, is will be displayed in a string that includes instructions on - * how to trigger the link, such as `{0} (ctrl + click)`. The specific instructions vary - * depending on OS, user settings, and localization. - */ - tooltip?: string; - } - - //#endregion - //#region @jrieken -> exclusive document filters export interface DocumentFilter { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 97793666ad8..d31f206b578 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -584,7 +584,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostTerminalService.registerLinkHandler(handler); }, registerTerminalLinkProvider(handler: vscode.TerminalLinkProvider): vscode.Disposable { - checkProposedApiEnabled(extension); return extHostTerminalService.registerLinkProvider(handler); }, registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider): vscode.Disposable { From a6561223e1097ccee832e7096e5403b00316fb3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 10 Aug 2020 19:30:34 +0200 Subject: [PATCH 063/736] rename --- src/vs/workbench/contrib/scm/browser/scmViewPane.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index a7f1d12a3ad..28f3f087e0d 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -465,7 +465,7 @@ class ResourceRenderer implements ICompressibleTreeRenderer { +class ListDelegate implements IListVirtualDelegate { constructor(private readonly inputRenderer: InputRenderer) { } @@ -1569,7 +1569,7 @@ export class SCMViewPane extends ViewPane { this._register(this.scmViewService.onDidChangeVisibleRepositories(() => this.updateActions())); this.inputRenderer = this.instantiationService.createInstance(InputRenderer, this.layoutCache, (input, height) => this.tree.updateElementHeight(input, height)); - const delegate = new ProviderListDelegate(this.inputRenderer); + const delegate = new ListDelegate(this.inputRenderer); const actionViewItemProvider = (action: IAction) => this.getActionViewItem(action); From 624900577e1b0dd041cd254c327d99fe58eb7b4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 10 Aug 2020 19:58:44 +0200 Subject: [PATCH 064/736] use classList --- src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts index db045b26c13..56587525a09 100644 --- a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts +++ b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts @@ -8,7 +8,7 @@ import { IAction, IActionRunner, IActionViewItemProvider } from 'vs/base/common/ import { IDisposable } from 'vs/base/common/lifecycle'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; -import { append, $, addClasses } from 'vs/base/browser/dom'; +import { append, $ } from 'vs/base/browser/dom'; import { Emitter } from 'vs/base/common/event'; import { BaseActionViewItem, IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { IActionProvider, DropdownMenu, IDropdownMenuOptions, ILabelRenderer } from 'vs/base/browser/ui/dropdown/dropdown'; @@ -67,7 +67,7 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem { classNames.push('codicon'); } - addClasses(this.element, ...classNames); + this.element.classList.add(...classNames); this.element.tabIndex = 0; this.element.setAttribute('role', 'button'); From 393a998f7d06e9d9198897dd0e1d5a0b95c22fca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 10 Aug 2020 20:39:57 +0200 Subject: [PATCH 065/736] interfaces for scm menus --- src/vs/workbench/contrib/scm/browser/menus.ts | 32 +++++++++---------- .../contrib/scm/browser/scmViewPane.ts | 2 +- src/vs/workbench/contrib/scm/common/scm.ts | 22 +++++++++++++ 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/menus.ts b/src/vs/workbench/contrib/scm/browser/menus.ts index 68f93b71938..aff10c40551 100644 --- a/src/vs/workbench/contrib/scm/browser/menus.ts +++ b/src/vs/workbench/contrib/scm/browser/menus.ts @@ -10,7 +10,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; import { IAction } from 'vs/base/common/actions'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { ISCMResource, ISCMResourceGroup, ISCMProvider, ISCMRepository, ISCMService } from 'vs/workbench/contrib/scm/common/scm'; +import { ISCMResource, ISCMResourceGroup, ISCMProvider, ISCMRepository, ISCMService, ISCMMenus, ISCMRepositoryMenus } from 'vs/workbench/contrib/scm/common/scm'; import { equals } from 'vs/base/common/arrays'; import { ISplice } from 'vs/base/common/sequence'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -20,7 +20,7 @@ function actionEquals(a: IAction, b: IAction): boolean { return a.id === b.id; } -export class SCMTitleMenu { +export class SCMTitleMenu implements IDisposable { private _actions: IAction[] = []; get actions(): IAction[] { return this._actions; } @@ -75,7 +75,7 @@ interface IContextualResourceMenuItem { dispose(): void; } -class SCMMenusItem { +class SCMMenusItem implements IDisposable { private _resourceGroupMenu: IMenu | undefined; get resourceGroupMenu(): IMenu { @@ -152,19 +152,28 @@ class SCMMenusItem { } } -export class SCMRepositoryMenus implements IDisposable { +export class SCMRepositoryMenus implements ISCMRepositoryMenus, IDisposable { private contextKeyService: IContextKeyService; readonly titleMenu: SCMTitleMenu; - private repositoryMenu: IMenu | undefined; private readonly resourceGroups: ISCMResourceGroup[] = []; private readonly resourceGroupMenusItems = new Map(); + private _repositoryMenu: IMenu | undefined; + get repositoryMenu(): IMenu { + if (!this._repositoryMenu) { + this._repositoryMenu = this.menuService.createMenu(MenuId.SCMSourceControl, this.contextKeyService); + this.disposables.add(this._repositoryMenu); + } + + return this._repositoryMenu; + } + private readonly disposables = new DisposableStore(); constructor( - readonly provider: ISCMProvider, + provider: ISCMProvider, @IContextKeyService contextKeyService: IContextKeyService, @IInstantiationService instantiationService: IInstantiationService, @IMenuService private readonly menuService: IMenuService @@ -181,15 +190,6 @@ export class SCMRepositoryMenus implements IDisposable { this.onDidSpliceGroups({ start: 0, deleteCount: 0, toInsert: provider.groups.elements }); } - getRepositoryMenu(): IMenu { - if (!this.repositoryMenu) { - this.repositoryMenu = this.menuService.createMenu(MenuId.SCMSourceControl, this.contextKeyService); - this.disposables.add(this.repositoryMenu); - } - - return this.repositoryMenu; - } - getResourceGroupMenu(group: ISCMResourceGroup): IMenu { return this.getOrCreateResourceGroupMenusItem(group).resourceGroupMenu; } @@ -233,7 +233,7 @@ export class SCMRepositoryMenus implements IDisposable { } } -export class SCMMenus { +export class SCMMenus implements ISCMMenus, IDisposable { readonly titleMenu: SCMTitleMenu; private readonly disposables = new DisposableStore(); diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 28f3f087e0d..21087ca7ffb 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -1790,7 +1790,7 @@ export class SCMViewPane extends ViewPane { if (isSCMRepository(element)) { const menus = this.menus.getRepositoryMenus(element.provider); - const menu = menus.getRepositoryMenu(); + const menu = menus.repositoryMenu; context = element.provider; [actions, disposable] = collectContextMenuActions(menu, this.contextMenuService); } else if (isSCMInput(element)) { diff --git a/src/vs/workbench/contrib/scm/common/scm.ts b/src/vs/workbench/contrib/scm/common/scm.ts index ccad186a67e..24f44b4ed63 100644 --- a/src/vs/workbench/contrib/scm/common/scm.ts +++ b/src/vs/workbench/contrib/scm/common/scm.ts @@ -9,6 +9,8 @@ import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Command } from 'vs/editor/common/modes'; import { ISequence } from 'vs/base/common/sequence'; +import { IAction } from 'vs/base/common/actions'; +import { IMenu } from 'vs/platform/actions/common/actions'; export const VIEWLET_ID = 'workbench.view.scm'; export const VIEW_PANE_ID = 'workbench.scm'; @@ -114,6 +116,26 @@ export interface ISCMService { registerSCMProvider(provider: ISCMProvider): ISCMRepository; } +export interface ISCMTitleMenu { + readonly actions: IAction[]; + readonly secondaryActions: IAction[]; + readonly onDidChangeTitle: Event; + readonly menu: IMenu; +} + +export interface ISCMRepositoryMenus { + readonly titleMenu: ISCMTitleMenu; + readonly repositoryMenu: IMenu; + getResourceGroupMenu(group: ISCMResourceGroup): IMenu; + getResourceMenu(resource: ISCMResource): IMenu; + getResourceFolderMenu(group: ISCMResourceGroup): IMenu; +} + +export interface ISCMMenus { + readonly titleMenu: ISCMTitleMenu; + getRepositoryMenus(provider: ISCMProvider): ISCMRepositoryMenus; +} + export const ISCMViewService = createDecorator('scmView'); export interface ISCMViewVisibleRepositoryChangeEvent { From daebb20c2cea55da8215b6d6bb81066f46f33b3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 10 Aug 2020 21:04:37 +0200 Subject: [PATCH 066/736] scm: move menus to view service --- .../contrib/scm/browser/scm.contribution.ts | 2 +- .../scm/browser/scmRepositoryRenderer.ts | 7 ++- .../contrib/scm/browser/scmViewPane.ts | 51 ++++++++----------- .../scm/{common => browser}/scmViewService.ts | 13 ++++- src/vs/workbench/contrib/scm/browser/util.ts | 34 ++++++++++++- src/vs/workbench/contrib/scm/common/scm.ts | 2 +- 6 files changed, 70 insertions(+), 39 deletions(-) rename src/vs/workbench/contrib/scm/{common => browser}/scmViewService.ts (85%) diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index f522e31c5a2..9b2876239e8 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -24,7 +24,7 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; import { Codicon } from 'vs/base/common/codicons'; import { SCMViewPane } from 'vs/workbench/contrib/scm/browser/scmViewPane'; -import { SCMViewService } from 'vs/workbench/contrib/scm/common/scmViewService'; +import { SCMViewService } from 'vs/workbench/contrib/scm/browser/scmViewService'; ModesRegistry.registerLanguage({ id: 'scminput', diff --git a/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts b/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts index 4d7514db007..1ab1da3ab69 100644 --- a/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts +++ b/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts @@ -7,12 +7,11 @@ import 'vs/css!./media/scm'; import { basename } from 'vs/base/common/resources'; import { IDisposable, Disposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle'; import { append, $, addClass, toggleClass } from 'vs/base/browser/dom'; -import { ISCMRepository } from 'vs/workbench/contrib/scm/common/scm'; +import { ISCMRepository, ISCMViewService } from 'vs/workbench/contrib/scm/common/scm'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IAction, IActionViewItemProvider } from 'vs/base/common/actions'; -import { SCMMenus } from './menus'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { connectPrimaryMenu, StatusBarAction } from './util'; import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; @@ -39,7 +38,7 @@ export class RepositoryRenderer implements ICompressibleTreeRenderer { menuPrimaryActions = primary; menuSecondaryActions = secondary; diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 21087ca7ffb..0106060492d 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -21,7 +21,6 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { MenuItemAction, IMenuService } from 'vs/platform/actions/common/actions'; import { IAction, IActionViewItem, ActionRunner, Action, RadioGroup, Separator, SubmenuAction, IActionViewItemProvider } from 'vs/base/common/actions'; -import { SCMMenus, SCMTitleMenu } from './menus'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { IThemeService, LIGHT, registerThemingParticipant, IFileIconTheme } from 'vs/platform/theme/common/themeService'; import { isSCMResource, isSCMResourceGroup, connectPrimaryMenuToInlineActionBar, isSCMRepository, isSCMInput, collectContextMenuActions, StatusBarAction, StatusBarActionViewItem } from './util'; @@ -244,7 +243,7 @@ class ResourceGroupRenderer implements ICompressibleTreeRenderer, - private menus: SCMMenus, private inputRenderer: InputRenderer, private _mode: ViewModelMode, private _sortKey: ViewModelSortKey, @@ -988,20 +986,20 @@ class ViewModel { getViewActions(): IAction[] { if (this.scmViewService.visibleRepositories.length === 0) { - return this.menus.titleMenu.actions; + return this.scmViewService.menus.titleMenu.actions; } if (this.alwaysShowRepositories || this.scmViewService.visibleRepositories.length !== 1) { return []; } - const menus = this.menus.getRepositoryMenus(this.scmViewService.visibleRepositories[0].provider); + const menus = this.scmViewService.menus.getRepositoryMenus(this.scmViewService.visibleRepositories[0].provider); return menus.titleMenu.actions; } getViewSecondaryActions(): IAction[] { if (this.scmViewService.visibleRepositories.length === 0) { - return this.menus.titleMenu.secondaryActions; + return this.scmViewService.menus.titleMenu.secondaryActions; } if (!this.viewSubMenuAction) { @@ -1013,7 +1011,7 @@ class ViewModel { return this.viewSubMenuAction.actions; } - const menus = this.menus.getRepositoryMenus(this.scmViewService.visibleRepositories[0].provider); + const menus = this.scmViewService.menus.getRepositoryMenus(this.scmViewService.visibleRepositories[0].provider); const secondaryActions = menus.titleMenu.secondaryActions; if (secondaryActions.length === 0) { @@ -1513,9 +1511,7 @@ export class SCMViewPane extends ViewPane { private tree!: WorkbenchCompressibleObjectTree; private viewModel!: ViewModel; private listLabels!: ResourceLabels; - private menus!: SCMMenus; private inputRenderer!: InputRenderer; - private genericTitleMenu: SCMTitleMenu; private toggleViewModelModeAction: ToggleViewModeAction | undefined; constructor( @@ -1540,8 +1536,7 @@ export class SCMViewPane extends ViewPane { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this._register(Event.any(this.scmService.onDidAddRepository, this.scmService.onDidRemoveRepository)(() => this._onDidChangeViewWelcomeState.fire())); - this.genericTitleMenu = instantiationService.createInstance(SCMTitleMenu); - this._register(this.genericTitleMenu.onDidChangeTitle(this.updateActions, this)); + this._register(this.scmViewService.menus.titleMenu.onDidChangeTitle(this.updateActions, this)); } protected renderBody(container: HTMLElement): void { @@ -1562,10 +1557,6 @@ export class SCMViewPane extends ViewPane { this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.providerCountBadge'))(updateProviderCountVisibility)); updateProviderCountVisibility(); - this.menus = this.instantiationService.createInstance(SCMMenus); - this._register(this.menus); - this._register(this.menus.titleMenu.onDidChangeTitle(this.updateActions, this)); - this._register(this.scmViewService.onDidChangeVisibleRepositories(() => this.updateActions())); this.inputRenderer = this.instantiationService.createInstance(InputRenderer, this.layoutCache, (input, height) => this.tree.updateElementHeight(input, height)); @@ -1581,10 +1572,10 @@ export class SCMViewPane extends ViewPane { this._register(actionRunner.onDidBeforeRun(() => this.tree.domFocus())); const renderers = [ - this.instantiationService.createInstance(RepositoryRenderer, actionViewItemProvider, this.menus), + this.instantiationService.createInstance(RepositoryRenderer, actionViewItemProvider), this.inputRenderer, - this.instantiationService.createInstance(ResourceGroupRenderer, actionViewItemProvider, this.menus), - this.instantiationService.createInstance(ResourceRenderer, () => this.viewModel, this.listLabels, actionViewItemProvider, actionRunner, this.menus) + this.instantiationService.createInstance(ResourceGroupRenderer, actionViewItemProvider), + this.instantiationService.createInstance(ResourceRenderer, () => this.viewModel, this.listLabels, actionViewItemProvider, actionRunner) ]; const filter = new SCMTreeFilter(); @@ -1625,7 +1616,7 @@ export class SCMViewPane extends ViewPane { viewMode = storageMode; } - this.viewModel = this.instantiationService.createInstance(ViewModel, this.tree, this.menus, this.inputRenderer, viewMode, ViewModelSortKey.Path); + this.viewModel = this.instantiationService.createInstance(ViewModel, this.tree, this.inputRenderer, viewMode, ViewModelSortKey.Path); this._register(this.viewModel); addClass(this.listContainer, 'file-icon-themable-tree'); @@ -1789,28 +1780,28 @@ export class SCMViewPane extends ViewPane { let disposable: IDisposable = Disposable.None; if (isSCMRepository(element)) { - const menus = this.menus.getRepositoryMenus(element.provider); + const menus = this.scmViewService.menus.getRepositoryMenus(element.provider); const menu = menus.repositoryMenu; context = element.provider; [actions, disposable] = collectContextMenuActions(menu, this.contextMenuService); } else if (isSCMInput(element)) { // noop } else if (isSCMResourceGroup(element)) { - const menus = this.menus.getRepositoryMenus(element.provider); + const menus = this.scmViewService.menus.getRepositoryMenus(element.provider); const menu = menus.getResourceGroupMenu(element); [actions, disposable] = collectContextMenuActions(menu, this.contextMenuService); } else if (ResourceTree.isResourceNode(element)) { if (element.element) { - const menus = this.menus.getRepositoryMenus(element.element.resourceGroup.provider); + const menus = this.scmViewService.menus.getRepositoryMenus(element.element.resourceGroup.provider); const menu = menus.getResourceMenu(element.element); [actions, disposable] = collectContextMenuActions(menu, this.contextMenuService); } else { - const menus = this.menus.getRepositoryMenus(element.context.provider); + const menus = this.scmViewService.menus.getRepositoryMenus(element.context.provider); const menu = menus.getResourceFolderMenu(element.context); [actions, disposable] = collectContextMenuActions(menu, this.contextMenuService); } } else { - const menus = this.menus.getRepositoryMenus(element.resourceGroup.provider); + const menus = this.scmViewService.menus.getRepositoryMenus(element.resourceGroup.provider); const menu = menus.getResourceMenu(element); [actions, disposable] = collectContextMenuActions(menu, this.contextMenuService); } diff --git a/src/vs/workbench/contrib/scm/common/scmViewService.ts b/src/vs/workbench/contrib/scm/browser/scmViewService.ts similarity index 85% rename from src/vs/workbench/contrib/scm/common/scmViewService.ts rename to src/vs/workbench/contrib/scm/browser/scmViewService.ts index 2ae341930d8..5f718aacfa1 100644 --- a/src/vs/workbench/contrib/scm/common/scmViewService.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewService.ts @@ -5,13 +5,17 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { Emitter } from 'vs/base/common/event'; -import { ISCMViewService, ISCMRepository, ISCMService, ISCMViewVisibleRepositoryChangeEvent } from './scm'; +import { ISCMViewService, ISCMRepository, ISCMService, ISCMViewVisibleRepositoryChangeEvent, ISCMMenus } from 'vs/workbench/contrib/scm/common/scm'; import { Iterable } from 'vs/base/common/iterator'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { SCMMenus } from 'vs/workbench/contrib/scm/browser/menus'; export class SCMViewService implements ISCMViewService { declare readonly _serviceBrand: undefined; + readonly menus: ISCMMenus; + private disposables = new DisposableStore(); private _visibleRepositoriesSet = new Set(); @@ -50,7 +54,12 @@ export class SCMViewService implements ISCMViewService { private _onDidChangeVisibleRepositories = new Emitter(); readonly onDidChangeVisibleRepositories = this._onDidChangeVisibleRepositories.event; - constructor(@ISCMService scmService: ISCMService) { + constructor( + @ISCMService scmService: ISCMService, + @IInstantiationService instantiationService: IInstantiationService + ) { + this.menus = instantiationService.createInstance(SCMMenus); + scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables); scmService.onDidRemoveRepository(this.onDidRemoveRepository, this, this.disposables); diff --git a/src/vs/workbench/contrib/scm/browser/util.ts b/src/vs/workbench/contrib/scm/browser/util.ts index 1b6dcfda1f7..d44158bb62a 100644 --- a/src/vs/workbench/contrib/scm/browser/util.ts +++ b/src/vs/workbench/contrib/scm/browser/util.ts @@ -7,10 +7,14 @@ import { ISCMResource, ISCMRepository, ISCMResourceGroup, ISCMInput } from 'vs/w import { IMenu } from 'vs/platform/actions/common/actions'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { IDisposable, Disposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IAction } from 'vs/base/common/actions'; +import { Action, IAction } from 'vs/base/common/actions'; import { createAndFillInActionBarActions, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { equals } from 'vs/base/common/arrays'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { renderCodicons } from 'vs/base/common/codicons'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { Command } from 'vs/editor/common/modes'; export function isSCMRepository(element: any): element is ISCMRepository { return !!(element as ISCMRepository).provider && typeof (element as ISCMRepository).setSelected === 'function'; @@ -74,3 +78,31 @@ export function collectContextMenuActions(menu: IMenu, contextMenuService: ICont const disposable = createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, { primary, secondary: actions }, contextMenuService, g => /^inline/.test(g)); return [actions, disposable]; } + +export class StatusBarAction extends Action { + + constructor( + private command: Command, + private commandService: ICommandService + ) { + super(`statusbaraction{${command.id}}`, command.title, '', true); + this.tooltip = command.tooltip || ''; + } + + run(): Promise { + return this.commandService.executeCommand(this.command.id, ...(this.command.arguments || [])); + } +} + +export class StatusBarActionViewItem extends ActionViewItem { + + constructor(action: StatusBarAction) { + super(null, action, {}); + } + + updateLabel(): void { + if (this.options.label && this.label) { + this.label.innerHTML = renderCodicons(escape(this.getAction().label)); + } + } +} diff --git a/src/vs/workbench/contrib/scm/common/scm.ts b/src/vs/workbench/contrib/scm/common/scm.ts index 24f44b4ed63..9a123a3899f 100644 --- a/src/vs/workbench/contrib/scm/common/scm.ts +++ b/src/vs/workbench/contrib/scm/common/scm.ts @@ -145,7 +145,7 @@ export interface ISCMViewVisibleRepositoryChangeEvent { export interface ISCMViewService { readonly _serviceBrand: undefined; - + readonly menus: ISCMMenus; visibleRepositories: ISCMRepository[]; readonly onDidChangeVisibleRepositories: Event; } From 4341cbe2eb837ae1e393c166b97994c2ad2cc978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 10 Aug 2020 21:25:49 +0200 Subject: [PATCH 067/736] fix escape usage --- src/vs/workbench/contrib/scm/browser/util.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/scm/browser/util.ts b/src/vs/workbench/contrib/scm/browser/util.ts index d44158bb62a..228614a12d1 100644 --- a/src/vs/workbench/contrib/scm/browser/util.ts +++ b/src/vs/workbench/contrib/scm/browser/util.ts @@ -15,6 +15,7 @@ import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { renderCodicons } from 'vs/base/common/codicons'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { Command } from 'vs/editor/common/modes'; +import { escape } from 'vs/base/common/strings'; export function isSCMRepository(element: any): element is ISCMRepository { return !!(element as ISCMRepository).provider && typeof (element as ISCMRepository).setSelected === 'function'; From 53cdcdd4d9dc6565085aaf18cfae3af2540680a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 10 Aug 2020 21:26:05 +0200 Subject: [PATCH 068/736] list: indexOf --- src/vs/base/browser/ui/list/listView.ts | 4 ++++ src/vs/base/browser/ui/list/listWidget.ts | 4 ++++ 2 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 5cecaeb44e4..223eb6e437a 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -602,6 +602,10 @@ export class ListView implements ISpliceable, IDisposable { return this.items[index].element; } + indexOf(element: T): number { + return this.items.findIndex(item => item.element === element); + } + domElement(index: number): HTMLElement | null { const row = this.items[index].row; return row && row.domNode; diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index d7e466466bd..252150b28d7 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -1323,6 +1323,10 @@ export class List implements ISpliceable, IThemable, IDisposable { return this.view.element(index); } + indexOf(element: T): number { + return this.view.indexOf(element); + } + get length(): number { return this.view.length; } From 07b7e993da6b65f0bc361bd27a95dd3f0179a4c7 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 10 Aug 2020 12:43:42 -0700 Subject: [PATCH 069/736] fixes #104224 --- src/vs/base/browser/ui/menu/menu.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index 028c8c157be..a150e286875 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -791,7 +791,12 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { private cleanupExistingSubmenu(force: boolean): void { if (this.parentData.submenu && (force || (this.parentData.submenu !== this.mysubmenu))) { - this.parentData.submenu.dispose(); + + // disposal may throw if the submenu has already been removed + try { + this.parentData.submenu.dispose(); + } catch { } + this.parentData.submenu = undefined; this.updateAriaExpanded('false'); if (this.submenuContainer) { From d613d4ecbb3b3d127484fb00856f178d719ea069 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 10 Aug 2020 12:57:43 -0700 Subject: [PATCH 070/736] Hide hovers when widget manager is disposed Fixes #101845 --- .../workbench/contrib/terminal/browser/widgets/widgetManager.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/terminal/browser/widgets/widgetManager.ts b/src/vs/workbench/contrib/terminal/browser/widgets/widgetManager.ts index b5bf843ffc7..f84bacb1cf5 100644 --- a/src/vs/workbench/contrib/terminal/browser/widgets/widgetManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/widgets/widgetManager.ts @@ -23,6 +23,7 @@ export class TerminalWidgetManager implements IDisposable { } dispose(): void { + this.hideHovers(); if (this._container && this._container.parentElement) { this._container.parentElement.removeChild(this._container); this._container = undefined; From 851c56b41c5794ee4f404e4660d7cd80ca152a32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 10 Aug 2020 22:13:54 +0200 Subject: [PATCH 071/736] Revert "use classList" This reverts commit 624900577e1b0dd041cd254c327d99fe58eb7b4a. --- src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts index 56587525a09..db045b26c13 100644 --- a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts +++ b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts @@ -8,7 +8,7 @@ import { IAction, IActionRunner, IActionViewItemProvider } from 'vs/base/common/ import { IDisposable } from 'vs/base/common/lifecycle'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; -import { append, $ } from 'vs/base/browser/dom'; +import { append, $, addClasses } from 'vs/base/browser/dom'; import { Emitter } from 'vs/base/common/event'; import { BaseActionViewItem, IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { IActionProvider, DropdownMenu, IDropdownMenuOptions, ILabelRenderer } from 'vs/base/browser/ui/dropdown/dropdown'; @@ -67,7 +67,7 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem { classNames.push('codicon'); } - this.element.classList.add(...classNames); + addClasses(this.element, ...classNames); this.element.tabIndex = 0; this.element.setAttribute('role', 'button'); From be8947b92d40fa963507c45870328538da39537f Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 10 Aug 2020 12:41:26 -0700 Subject: [PATCH 072/736] inspect layout --- .../contrib/notebook/browser/contrib/coreActions.ts | 11 ++++++++++- .../notebook/browser/viewModel/notebookViewModel.ts | 10 ---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 0a0a8fb1f3f..6fa682c7d80 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -1527,7 +1527,16 @@ registerAction2(class extends Action2 { const activeEditorContext = this.getActiveEditorContext(accessor); if (activeEditorContext) { - activeEditorContext.notebookEditor.viewModel!.inspectLayout(); + const viewModel = activeEditorContext.notebookEditor.viewModel!; + console.log('--- notebook ---'); + console.log(viewModel.layoutInfo); + console.log('--- cells ---'); + for (let i = 0; i < viewModel.length; i++) { + const cell = viewModel.viewCells[i] as CellViewModel; + console.log(`--- cell: ${cell.handle} ---`); + console.log(cell.layoutInfo); + } + } } }); diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts index 47739daac44..8fa99689921 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts @@ -349,16 +349,6 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD }); } - inspectLayout() { - console.log('--- notebook ---\n'); - console.log(this.layoutInfo); - console.log('--- cells ---'); - this.viewCells.forEach(cell => { - console.log(`--- cell: ${cell.handle} ---\n`); - console.log((cell as (CodeCellViewModel | MarkdownCellViewModel)).layoutInfo); - }); - } - setFocus(focused: boolean) { this._focused = focused; } From 5cadd5477e047f318f5effbf4c7e25661f35a40f Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 10 Aug 2020 12:42:17 -0700 Subject: [PATCH 073/736] wrong preview rendering after drag and drop --- .../contrib/notebook/browser/view/renderers/cellRenderer.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 88f675c66ea..1fe96fd2647 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -472,10 +472,6 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR templateData.currentEditor = undefined; templateData.editorPart!.style.display = 'none'; templateData.cellContainer.innerHTML = ''; - const renderedHTML = element.getHTML(); - if (renderedHTML) { - templateData.cellContainer.appendChild(renderedHTML); - } if (height === undefined) { return; From 9294fd5b944bd37635bc4588b0cf9ac670f582a2 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 10 Aug 2020 12:43:38 -0700 Subject: [PATCH 074/736] support move multiple cells. --- .../notebook/browser/notebookBrowser.ts | 2 +- .../notebook/browser/notebookEditorWidget.ts | 25 +++++---- .../browser/view/renderers/cellRenderer.ts | 53 ++++--------------- .../browser/viewModel/notebookViewModel.ts | 12 ++++- .../contrib/notebook/common/model/cellEdit.ts | 7 +-- .../common/model/notebookTextModel.ts | 20 +++---- .../notebook/test/notebookViewModel.test.ts | 10 ++-- .../notebook/test/testNotebookEditor.ts | 2 +- 8 files changed, 54 insertions(+), 77 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 58fdb4b4a8e..8f14fcaddf0 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -259,7 +259,7 @@ export interface INotebookEditor extends IEditor { /** * Move a cell to a specific position */ - moveCellToIdx(cell: ICellViewModel, index: number): Promise; + moveCellsToIdx(index: number, length: number, toIdx: number): Promise; /** * Focus the container of a cell (the monaco editor inside is not focused). diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 40d5380bd39..f1894d442ae 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1263,7 +1263,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } const newIdx = index + 2; // This is the adjustment for the index before the cell has been "removed" from its original index - return this._moveCellToIndex(index, newIdx); + return this._moveCellToIndex(index, 1, newIdx); } async moveCellUp(cell: ICellViewModel): Promise { @@ -1277,7 +1277,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } const newIdx = index - 1; - return this._moveCellToIndex(index, newIdx); + return this._moveCellToIndex(index, 1, newIdx); } async moveCell(cell: ICellViewModel, relativeToCell: ICellViewModel, direction: 'above' | 'below'): Promise { @@ -1293,34 +1293,33 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor const relativeToIndex = this._notebookViewModel!.getCellIndex(relativeToCell); const newIdx = direction === 'above' ? relativeToIndex : relativeToIndex + 1; - return this._moveCellToIndex(originalIdx, newIdx); + return this._moveCellToIndex(originalIdx, 1, newIdx); } - async moveCellToIdx(cell: ICellViewModel, index: number): Promise { + async moveCellsToIdx(index: number, length: number, toIdx: number): Promise { if (!this._notebookViewModel!.metadata.editable) { return null; } - const originalIdx = this._notebookViewModel!.getCellIndex(cell); - return this._moveCellToIndex(originalIdx, index); + return this._moveCellToIndex(index, length, toIdx); } /** * @param index The current index of the cell - * @param newIdx The desired index, in an index scheme for the state of the tree before the current cell has been "removed". + * @param desiredIndex The desired index, in an index scheme for the state of the tree before the current cell has been "removed". * @example to move the cell from index 0 down one spot, call with (0, 2) */ - private async _moveCellToIndex(index: number, newIdx: number): Promise { - if (index < newIdx) { + private async _moveCellToIndex(index: number, length: number, desiredIndex: number): Promise { + if (index < desiredIndex) { // The cell is moving "down", it will free up one index spot and consume a new one - newIdx--; + desiredIndex -= length; } - if (index === newIdx) { + if (index === desiredIndex) { return null; } - if (!this._notebookViewModel!.moveCellToIdx(index, newIdx, true)) { + if (!this._notebookViewModel!.moveCellToIdx(index, length, desiredIndex, true)) { throw new Error('Notebook Editor move cell, index out of range'); } @@ -1330,7 +1329,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor r(null); } - const viewCell = this._notebookViewModel!.viewCells[newIdx]; + const viewCell = this._notebookViewModel!.viewCells[desiredIndex]; this._list?.revealElementInView(viewCell); r(viewCell); }); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 1fe96fd2647..a4357dd5b47 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -672,6 +672,7 @@ export class CellDragAndDropController extends Disposable { } let draggedCells: ICellViewModel[] = [draggedCell]; + let draggedCellRange: [number, number] = [this.notebookEditor.viewModel!.getCellIndex(draggedCell), 1]; if (draggedCell.cellKind === CellKind.Markdown) { const currCellIndex = this.notebookEditor.viewModel!.getCellIndex(draggedCell); @@ -680,6 +681,7 @@ export class CellDragAndDropController extends Disposable { if (nextVisibleCellIndex > currCellIndex + 1) { // folding ;) draggedCells = this.notebookEditor.viewModel!.viewCells.slice(currCellIndex, nextVisibleCellIndex); + draggedCellRange = [currCellIndex, nextVisibleCellIndex - currCellIndex]; } } @@ -699,7 +701,15 @@ export class CellDragAndDropController extends Disposable { if (isCopy) { this.copyCells(draggedCells, event.draggedOverCell, dropDirection); } else { - this.moveCells(draggedCells, event.draggedOverCell, dropDirection); + const viewModel = this.notebookEditor.viewModel!; + let originalToIdx = viewModel.getCellIndex(event.draggedOverCell); + if (dropDirection === 'below') { + const relativeToIndex = viewModel.getCellIndex(event.draggedOverCell); + const newIdx = viewModel.getNextVisibleCellIndex(relativeToIndex); + originalToIdx = newIdx; + } + + this.notebookEditor.moveCellsToIdx(draggedCellRange[0], draggedCellRange[1], originalToIdx); } } @@ -745,47 +755,6 @@ export class CellDragAndDropController extends Disposable { })); } - private async moveCells(draggedCells: ICellViewModel[], ontoCell: ICellViewModel, direction: 'above' | 'below') { - const viewModel = this.notebookEditor.viewModel; - - if (!viewModel) { - return; - } - - this.notebookEditor.textModel!.pushStackElement('Move Cells'); - if (direction === 'above') { - for (let i = 0; i < draggedCells.length; i++) { - await this.notebookEditor.moveCellToIdx(draggedCells[i], viewModel.getCellIndex(ontoCell)); - } - } else { - const relativeToIndex = viewModel.getCellIndex(ontoCell); - const newIdx = viewModel.getNextVisibleCellIndex(relativeToIndex); - - if (relativeToIndex + 1 === newIdx) { - // no folding - for (let i = draggedCells.length - 1; i >= 0; i--) { - const newIdx = viewModel.getCellIndex(ontoCell) + 1; - await this.notebookEditor.moveCellToIdx(draggedCells[i], newIdx); - } - } else { - // there is folding - if (newIdx === viewModel.length) { - // insert to the end - for (let i = 0; i < draggedCells.length; i++) { - await this.notebookEditor.moveCellToIdx(draggedCells[i], this.notebookEditor.viewModel!.length); - } - } else { - ontoCell = viewModel.viewCells[newIdx]; - // drop above ontoCell - for (let i = 0; i < draggedCells.length; i++) { - await this.notebookEditor.moveCellToIdx(draggedCells[i], viewModel.getCellIndex(ontoCell)); - } - } - } - } - this.notebookEditor.textModel!.pushStackElement('Move Cells'); - } - private copyCells(draggedCells: ICellViewModel[], ontoCell: ICellViewModel, direction: 'above' | 'below') { this.notebookEditor.textModel!.pushStackElement('Copy Cells'); let firstNewCell: ICellViewModel | undefined = undefined; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts index 8fa99689921..2f2f79f1746 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts @@ -647,13 +647,21 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD this._notebook.deleteCell2(index, synchronous, pushUndoStop, this.selectionHandles, endSelections); } - moveCellToIdx(index: number, newIdx: number, synchronous: boolean, pushedToUndoStack: boolean = true): boolean { + /** + * + * @param index + * @param length + * @param newIdx in an index scheme for the state of the tree after the current cell has been "removed" + * @param synchronous + * @param pushedToUndoStack + */ + moveCellToIdx(index: number, length: number, newIdx: number, synchronous: boolean, pushedToUndoStack: boolean = true): boolean { const viewCell = this.viewCells[index] as CellViewModel; if (!viewCell) { return false; } - this._notebook.moveCellToIdx2(index, newIdx, synchronous, pushedToUndoStack, undefined, [viewCell.handle]); + this._notebook.moveCellToIdx2(index, length, newIdx, synchronous, pushedToUndoStack, undefined, [viewCell.handle]); return true; } diff --git a/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts b/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts index 0d609102629..aaf080d20d1 100644 --- a/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts +++ b/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts @@ -13,7 +13,7 @@ import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/mode export interface ITextCellEditingDelegate { insertCell?(index: number, cell: NotebookCellTextModel): void; deleteCell?(index: number): void; - moveCell?(fromIndex: number, toIndex: number, beforeSelections: number[] | undefined, endSelections: number[] | undefined): void; + moveCell?(fromIndex: number, length: number, toIndex: number, beforeSelections: number[] | undefined, endSelections: number[] | undefined): void; emitSelections(selections: number[]): void; } @@ -100,6 +100,7 @@ export class MoveCellEdit implements IResourceUndoRedoElement { constructor( public resource: URI, private fromIndex: number, + private length: number, private toIndex: number, private editingDelegate: ITextCellEditingDelegate, private beforedSelections: number[] | undefined, @@ -112,7 +113,7 @@ export class MoveCellEdit implements IResourceUndoRedoElement { throw new Error('Notebook Move Cell not implemented for Undo/Redo'); } - this.editingDelegate.moveCell(this.toIndex, this.fromIndex, this.endSelections, this.beforedSelections); + this.editingDelegate.moveCell(this.toIndex, this.length, this.fromIndex, this.endSelections, this.beforedSelections); if (this.beforedSelections) { this.editingDelegate.emitSelections(this.beforedSelections); } @@ -123,7 +124,7 @@ export class MoveCellEdit implements IResourceUndoRedoElement { throw new Error('Notebook Move Cell not implemented for Undo/Redo'); } - this.editingDelegate.moveCell(this.fromIndex, this.toIndex, this.beforedSelections, this.endSelections); + this.editingDelegate.moveCell(this.fromIndex, this.length, this.toIndex, this.beforedSelections, this.endSelections); if (this.endSelections) { this.editingDelegate.emitSelections(this.endSelections); } diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index a45d181df50..d48a7968a42 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -477,11 +477,11 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel } } - moveCellToIdx(index: number, newIdx: number, emitToExtHost: boolean = true) { + moveCellToIdx(index: number, length: number, newIdx: number, emitToExtHost: boolean = true) { this.assertIndex(index); this.assertIndex(newIdx); - const cells = this.cells.splice(index, 1); + const cells = this.cells.splice(index, length); this.cells.splice(newIdx, 0, ...cells); this.setDirty(true); this._onDidChangeContent.fire(); @@ -618,22 +618,22 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel } } - moveCellToIdx2(index: number, newIdx: number, synchronous: boolean, pushedToUndoStack: boolean, beforeSelections: number[] | undefined, endSelections: number[] | undefined): boolean { - const cell = this.cells[index]; + moveCellToIdx2(index: number, length: number, newIdx: number, synchronous: boolean, pushedToUndoStack: boolean, beforeSelections: number[] | undefined, endSelections: number[] | undefined): boolean { + const cells = this.cells.slice(index, index + length); if (pushedToUndoStack) { - this._operationManager.pushEditOperation(new MoveCellEdit(this.uri, index, newIdx, { - moveCell: (fromIndex: number, toIndex: number, beforeSelections: number[] | undefined, endSelections: number[] | undefined) => { - this.moveCellToIdx2(fromIndex, toIndex, true, false, beforeSelections, endSelections); + this._operationManager.pushEditOperation(new MoveCellEdit(this.uri, index, length, newIdx, { + moveCell: (fromIndex: number, length: number, toIndex: number, beforeSelections: number[] | undefined, endSelections: number[] | undefined) => { + this.moveCellToIdx2(fromIndex, length, toIndex, true, false, beforeSelections, endSelections); }, emitSelections: this._emitSelectionsDelegate.bind(this) }, beforeSelections, endSelections)); } - this.moveCellToIdx(index, newIdx); + this.moveCellToIdx(index, length, newIdx); // todo, we can't emit this change as it will create a new view model and that will hold // a new reference to the document, thus - this._onDidChangeCells.fire({ synchronous: synchronous, splices: [[index, 1, []]] }); - this._onDidChangeCells.fire({ synchronous: synchronous, splices: [[newIdx, 0, [cell]]] }); + this._onDidChangeCells.fire({ synchronous: synchronous, splices: [[index, length, []]] }); + this._onDidChangeCells.fire({ synchronous: synchronous, splices: [[newIdx, 0, cells]] }); if (endSelections) { this._emitSelections.fire(endSelections); } diff --git a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts index 60d7d3cfdf7..052842994fa 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts @@ -67,18 +67,18 @@ suite('NotebookViewModel', () => { [['//c'], 'javascript', CellKind.Code, [], { editable: true }], ], (editor, viewModel) => { - viewModel.moveCellToIdx(0, 0, false); + viewModel.moveCellToIdx(0, 1, 0, false); // no-op assert.equal(viewModel.viewCells[0].getText(), '//a'); assert.equal(viewModel.viewCells[1].getText(), '//b'); - viewModel.moveCellToIdx(0, 1, false); + viewModel.moveCellToIdx(0, 1, 1, false); // b, a, c assert.equal(viewModel.viewCells[0].getText(), '//b'); assert.equal(viewModel.viewCells[1].getText(), '//a'); assert.equal(viewModel.viewCells[2].getText(), '//c'); - viewModel.moveCellToIdx(0, 2, false); + viewModel.moveCellToIdx(0, 1, 2, false); // a, c, b assert.equal(viewModel.viewCells[0].getText(), '//a'); assert.equal(viewModel.viewCells[1].getText(), '//c'); @@ -98,12 +98,12 @@ suite('NotebookViewModel', () => { [['//c'], 'javascript', CellKind.Code, [], { editable: true }], ], (editor, viewModel) => { - viewModel.moveCellToIdx(1, 0, false); + viewModel.moveCellToIdx(1, 1, 0, false); // b, a, c assert.equal(viewModel.viewCells[0].getText(), '//b'); assert.equal(viewModel.viewCells[1].getText(), '//a'); - viewModel.moveCellToIdx(2, 0, false); + viewModel.moveCellToIdx(2, 1, 0, false); // c, b, a assert.equal(viewModel.viewCells[0].getText(), '//c'); assert.equal(viewModel.viewCells[1].getText(), '//b'); diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index a79ceb86034..bee4079ebd8 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -177,7 +177,7 @@ export class TestNotebookEditor implements INotebookEditor { throw new Error('Method not implemented.'); } - moveCellToIdx(cell: ICellViewModel, index: number): Promise { + async moveCellsToIdx(index: number, length: number, toIdx: number): Promise { throw new Error('Method not implemented.'); } From 5e697b348c0336d11a64ce0440aed6c40b104f1c Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 10 Aug 2020 13:30:53 -0700 Subject: [PATCH 075/736] fix #104397. --- .../contrib/notebook/browser/view/notebookCellList.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index 687a666c773..6a257986a44 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -398,6 +398,10 @@ export class NotebookCellList extends WorkbenchList implements ID const viewIndexInfo = this.hiddenRangesPrefixSum.getIndexOf(modelIndex); if (viewIndexInfo.remainder !== 0) { + if (modelIndex >= this.hiddenRangesPrefixSum.getTotalValue()) { + // it's already after the last hidden range + return modelIndex - (this.hiddenRangesPrefixSum.getTotalValue() - this.hiddenRangesPrefixSum.getCount()); + } return undefined; } else { return viewIndexInfo.index; @@ -413,6 +417,12 @@ export class NotebookCellList extends WorkbenchList implements ID const viewIndexInfo = this.hiddenRangesPrefixSum.getIndexOf(modelIndex); + if (viewIndexInfo.remainder !== 0) { + if (modelIndex >= this.hiddenRangesPrefixSum.getTotalValue()) { + return modelIndex - (this.hiddenRangesPrefixSum.getTotalValue() - this.hiddenRangesPrefixSum.getCount()); + } + } + return viewIndexInfo.index; } From 6dd0a9a64dbd9cbf032be18311eb97b42fea1d94 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 10 Aug 2020 12:12:37 -0700 Subject: [PATCH 076/736] Remove any casts --- .../src/languageFeatures/definitions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/definitions.ts b/extensions/typescript-language-features/src/languageFeatures/definitions.ts index 7c01400c206..6d6e4fc2851 100644 --- a/extensions/typescript-language-features/src/languageFeatures/definitions.ts +++ b/extensions/typescript-language-features/src/languageFeatures/definitions.ts @@ -39,10 +39,10 @@ export default class TypeScriptDefinitionProvider extends DefinitionProviderBase return response.body.definitions .map((location): vscode.DefinitionLink => { const target = typeConverters.Location.fromTextSpan(this.client.toResource(location.file), location); - if ((location as any).contextStart) { + if (location.contextStart && location.contextEnd) { return { originSelectionRange: span, - targetRange: typeConverters.Range.fromLocations((location as any).contextStart, (location as any).contextEnd), + targetRange: typeConverters.Range.fromLocations(location.contextStart, location.contextEnd), targetUri: target.uri, targetSelectionRange: target.range, }; From 7819af28ae11d99043ce8aa87497c13060a47670 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 10 Aug 2020 13:33:45 -0700 Subject: [PATCH 077/736] Pick up TS 4.0 rc for insiders --- 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 665553eeeae..5577dde9099 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.9.7" + "typescript": "^4.0.1-rc" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 102d128edb6..9e459157c1b 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@3.9.7: - version "3.9.7" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" - integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== +typescript@^4.0.1-rc: + version "4.0.1-rc" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.1-rc.tgz#8adc78223eae56fe71d906a5fa90c3543b07a677" + integrity sha512-TCkspT3dSKOykbzS3/WSK7pqU2h1d/lEO6i45Afm5Y3XNAEAo8YXTG3kHOQk/wFq/5uPyO1+X8rb/Q+g7UsxJw== From 62d2d606b1c41399b5c8cf1af477f29b53c95692 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Mon, 10 Aug 2020 14:17:45 -0700 Subject: [PATCH 078/736] decrease deep classifier run frequency to reduce bandwidth --- .github/workflows/deep-classifier-runner.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deep-classifier-runner.yml b/.github/workflows/deep-classifier-runner.yml index 4ac237e5f83..29d7103adff 100644 --- a/.github/workflows/deep-classifier-runner.yml +++ b/.github/workflows/deep-classifier-runner.yml @@ -1,7 +1,7 @@ name: "Deep Classifier: Runner" on: schedule: - - cron: 0/30 * * * * + - cron: 0 * * * * repository_dispatch: types: [trigger-deep-classifier-runner] @@ -24,7 +24,7 @@ jobs: uses: ./actions/classifier-deep/apply/fetch-sources with: # slightly overlapping to protect against issues slipping through the cracks if a run is delayed - from: 40 + from: 80 until: 5 configPath: classifier blobContainerName: vscode-issue-classifier From 2b353aac93774e1e1bed1f89634a967bdf324dc0 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 10 Aug 2020 14:11:30 -0700 Subject: [PATCH 079/736] Remove terminal link handlers Fixes #91606 --- src/vs/vscode.proposed.d.ts | 29 ---- .../api/browser/mainThreadTerminalService.ts | 21 +-- .../workbench/api/common/extHost.api.impl.ts | 4 - .../workbench/api/common/extHost.protocol.ts | 3 - .../api/common/extHostTerminalService.ts | 34 ---- .../browser/links/terminalLinkManager.ts | 58 +------ .../contrib/terminal/browser/terminal.ts | 17 -- .../terminal/browser/terminalInstance.ts | 8 +- .../terminal/browser/terminalService.ts | 47 +---- .../browser/links/terminalLinkManager.test.ts | 163 ------------------ 10 files changed, 9 insertions(+), 375 deletions(-) delete mode 100644 src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkManager.test.ts diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 5c43806131f..cdbaf2293d4 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -927,35 +927,6 @@ declare module 'vscode' { //#endregion - //#region Terminal link handlers https://github.com/microsoft/vscode/issues/91606 - - export namespace window { - /** - * Register a [TerminalLinkHandler](#TerminalLinkHandler) that can be used to intercept and - * handle links that are activated within terminals. - * @param handler The link handler being registered. - * @return A disposable that unregisters the link handler. - */ - export function registerTerminalLinkHandler(handler: TerminalLinkHandler): Disposable; - } - - /** - * Describes how to handle terminal links. - */ - export interface TerminalLinkHandler { - /** - * Handles a link that is activated within the terminal. - * - * @param terminal The terminal the link was activated on. - * @param link The text of the link activated. - * @return Whether the link was handled, if the link was handled this link will not be - * considered by any other extension or by the default built-in link handler. - */ - handleLink(terminal: Terminal, link: string): ProviderResult; - } - - //#endregion - //#region @jrieken -> exclusive document filters export interface DocumentFilter { diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index ffd7fb0faee..d518ec92f6e 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -9,7 +9,7 @@ import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceS import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { URI } from 'vs/base/common/uri'; import { StopWatch } from 'vs/base/common/stopwatch'; -import { ITerminalInstanceService, ITerminalService, ITerminalInstance, ITerminalBeforeHandleLinkEvent, ITerminalExternalLinkProvider, ITerminalLink } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalInstanceService, ITerminalService, ITerminalInstance, ITerminalExternalLinkProvider, ITerminalLink } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering'; @@ -25,7 +25,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape private readonly _toDispose = new DisposableStore(); private readonly _terminalProcessProxies = new Map(); private _dataEventTracker: TerminalDataEventTracker | undefined; - private _linkHandler: IDisposable | undefined; /** * A single shared terminal link provider for the exthost. When an ext registers a link * provider, this is registered with the terminal on the renderer side and all links are @@ -95,7 +94,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape public dispose(): void { this._toDispose.dispose(); - this._linkHandler?.dispose(); this._linkProvider?.dispose(); // TODO@Daniel: Should all the previously created terminals be disposed @@ -166,16 +164,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape } } - public $startHandlingLinks(): void { - this._linkHandler?.dispose(); - this._linkHandler = this._terminalService.addLinkHandler(this._remoteAuthority || '', e => this._handleLink(e)); - } - - public $stopHandlingLinks(): void { - this._linkHandler?.dispose(); - this._linkHandler = undefined; - } - public $startLinkProvider(): void { this._linkProvider?.dispose(); this._linkProvider = this._terminalService.registerLinkProvider(new ExtensionTerminalLinkProvider(this._proxy)); @@ -186,13 +174,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._linkProvider = undefined; } - private async _handleLink(e: ITerminalBeforeHandleLinkEvent): Promise { - if (!e.terminal) { - return false; - } - return this._proxy.$handleLink(e.terminal.id, e.link); - } - private _onActiveTerminalChanged(terminalId: number | null): void { this._proxy.$acceptActiveTerminalChanged(terminalId); } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index d31f206b578..77f89a398a7 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -579,10 +579,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I } return extHostTerminalService.createTerminal(nameOrOptions, shellPath, shellArgs); }, - registerTerminalLinkHandler(handler: vscode.TerminalLinkHandler): vscode.Disposable { - checkProposedApiEnabled(extension); - return extHostTerminalService.registerLinkHandler(handler); - }, registerTerminalLinkProvider(handler: vscode.TerminalLinkProvider): vscode.Disposable { return extHostTerminalService.registerLinkProvider(handler); }, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 6caf716b6c6..0ad328d3549 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -448,8 +448,6 @@ export interface MainThreadTerminalServiceShape extends IDisposable { $show(terminalId: number, preserveFocus: boolean): void; $startSendingDataEvents(): void; $stopSendingDataEvents(): void; - $startHandlingLinks(): void; - $stopHandlingLinks(): void; $startLinkProvider(): void; $stopLinkProvider(): void; $setEnvironmentVariableCollection(extensionIdentifier: string, persistent: boolean, collection: ISerializableEnvironmentVariableCollection | undefined): void; @@ -1434,7 +1432,6 @@ export interface ExtHostTerminalServiceShape { $acceptWorkspacePermissionsChanged(isAllowed: boolean): void; $getAvailableShells(): Promise; $getDefaultShellAndArgs(useAutomationShell: boolean): Promise; - $handleLink(id: number, link: string): Promise; $provideLinks(id: number, line: string): Promise; $activateLink(id: number, linkId: number): void; $initEnvironmentVariableCollections(collections: [string, ISerializableEnvironmentVariableCollection][]): void; diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index ed8fd570cdd..d49cb264c21 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -41,7 +41,6 @@ export interface IExtHostTerminalService extends ExtHostTerminalServiceShape { attachPtyToTerminal(id: number, pty: vscode.Pseudoterminal): void; getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string; getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string; - registerLinkHandler(handler: vscode.TerminalLinkHandler): vscode.Disposable; registerLinkProvider(provider: vscode.TerminalLinkProvider): vscode.Disposable; getEnvironmentVariableCollection(extension: IExtensionDescription, persistent?: boolean): vscode.EnvironmentVariableCollection; } @@ -318,7 +317,6 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ protected _environmentVariableCollections: Map = new Map(); private readonly _bufferer: TerminalDataBufferer; - private readonly _linkHandlers: Set = new Set(); private readonly _linkProviders: Set = new Set(); private readonly _terminalLinkCache: Map> = new Map(); private readonly _terminalLinkCancellationSource: Map = new Map(); @@ -559,19 +557,6 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ return id; } - public registerLinkHandler(handler: vscode.TerminalLinkHandler): vscode.Disposable { - this._linkHandlers.add(handler); - if (this._linkHandlers.size === 1 && this._linkProviders.size === 0) { - this._proxy.$startHandlingLinks(); - } - return new VSCodeDisposable(() => { - this._linkHandlers.delete(handler); - if (this._linkHandlers.size === 0 && this._linkProviders.size === 0) { - this._proxy.$stopHandlingLinks(); - } - }); - } - public registerLinkProvider(provider: vscode.TerminalLinkProvider): vscode.Disposable { this._linkProviders.add(provider); if (this._linkProviders.size === 1) { @@ -585,25 +570,6 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ }); } - public async $handleLink(id: number, link: string): Promise { - const terminal = this._getTerminalById(id); - if (!terminal) { - return false; - } - - // Call each handler synchronously so multiple handlers aren't triggered at once - const it = this._linkHandlers.values(); - let next = it.next(); - while (!next.done) { - const handled = await next.value.handleLink(terminal, link); - if (handled) { - return true; - } - next = it.next(); - } - return false; - } - public async $provideLinks(terminalId: number, line: string): Promise { const terminal = this._getTerminalById(terminalId); if (!terminal) { diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts index 64434e13548..00d3c14b542 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts @@ -16,11 +16,9 @@ import { IFileService } from 'vs/platform/files/common/files'; import { Terminal, IViewportRange, ILinkProvider } from 'xterm'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { posix, win32 } from 'vs/base/common/path'; -import { ITerminalBeforeHandleLinkEvent, LINK_INTERCEPT_THRESHOLD, ITerminalExternalLinkProvider, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalExternalLinkProvider, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { OperatingSystem, isMacintosh, OS } from 'vs/base/common/platform'; import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; -import { Emitter, Event } from 'vs/base/common/event'; -import { ILogService } from 'vs/platform/log/common/log'; import { TerminalProtocolLinkProvider } from 'vs/workbench/contrib/terminal/browser/links/terminalProtocolLinkProvider'; import { TerminalValidatedLocalLinkProvider, lineAndColumnClause, unixLocalLinkClause, winLocalLinkClause, winDrivePrefix, winLineAndColumnMatchIndex, unixLineAndColumnMatchIndex, lineAndColumnClauseGroupCount } from 'vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider'; import { TerminalWordLinkProvider } from 'vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider'; @@ -44,24 +42,9 @@ interface IPath { export class TerminalLinkManager extends DisposableStore { private _widgetManager: TerminalWidgetManager | undefined; private _processCwd: string | undefined; - private _hasBeforeHandleLinkListeners = false; private _standardLinkProviders: ILinkProvider[] = []; private _standardLinkProvidersDisposables: IDisposable[] = []; - protected static _LINK_INTERCEPT_THRESHOLD = LINK_INTERCEPT_THRESHOLD; - public static readonly LINK_INTERCEPT_THRESHOLD = TerminalLinkManager._LINK_INTERCEPT_THRESHOLD; - - private readonly _onBeforeHandleLink = this.add(new Emitter({ - onFirstListenerAdd: () => this._hasBeforeHandleLinkListeners = true, - onLastListenerRemove: () => this._hasBeforeHandleLinkListeners = false - })); - /** - * Allows intercepting links and handling them outside of the default link handler. When fired - * the listener has a set amount of time to handle the link or the default handler will fire. - * This was designed to only be handled by a single listener. - */ - public get onBeforeHandleLink(): Event { return this._onBeforeHandleLink.event; } - constructor( private _xterm: Terminal, private readonly _processManager: ITerminalProcessManager, @@ -69,14 +52,13 @@ export class TerminalLinkManager extends DisposableStore { @IEditorService private readonly _editorService: IEditorService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IFileService private readonly _fileService: IFileService, - @ILogService private readonly _logService: ILogService, @IInstantiationService private readonly _instantiationService: IInstantiationService ) { super(); // Protocol links const wrappedActivateCallback = this._wrapLinkHandler((_, link) => this._handleProtocolLink(link)); - const protocolProvider = this._instantiationService.createInstance(TerminalProtocolLinkProvider, this._xterm, wrappedActivateCallback, this._tooltipCallback2.bind(this)); + const protocolProvider = this._instantiationService.createInstance(TerminalProtocolLinkProvider, this._xterm, wrappedActivateCallback, this._tooltipCallback.bind(this)); this._standardLinkProviders.push(protocolProvider); // Validated local links @@ -87,19 +69,19 @@ export class TerminalLinkManager extends DisposableStore { this._processManager.os || OS, wrappedTextLinkActivateCallback, this._wrapLinkHandler.bind(this), - this._tooltipCallback2.bind(this), + this._tooltipCallback.bind(this), async (link, cb) => cb(await this._resolvePath(link))); this._standardLinkProviders.push(validatedProvider); } // Word links - const wordProvider = this._instantiationService.createInstance(TerminalWordLinkProvider, this._xterm, this._wrapLinkHandler.bind(this), this._tooltipCallback2.bind(this)); + const wordProvider = this._instantiationService.createInstance(TerminalWordLinkProvider, this._xterm, this._wrapLinkHandler.bind(this), this._tooltipCallback.bind(this)); this._standardLinkProviders.push(wordProvider); this._registerStandardLinkProviders(); } - private _tooltipCallback2(link: TerminalLink, viewportRange: IViewportRange, modifierDownCallback?: () => void, modifierUpCallback?: () => void) { + private _tooltipCallback(link: TerminalLink, viewportRange: IViewportRange, modifierDownCallback?: () => void, modifierUpCallback?: () => void) { if (!this._widgetManager) { return; } @@ -156,7 +138,7 @@ export class TerminalLinkManager extends DisposableStore { } public registerExternalLinkProvider(instance: ITerminalInstance, linkProvider: ITerminalExternalLinkProvider): IDisposable { - const wrappedLinkProvider = this._instantiationService.createInstance(TerminalExternalLinkProviderAdapter, this._xterm, instance, linkProvider, this._wrapLinkHandler.bind(this), this._tooltipCallback2.bind(this)); + const wrappedLinkProvider = this._instantiationService.createInstance(TerminalExternalLinkProviderAdapter, this._xterm, instance, linkProvider, this._wrapLinkHandler.bind(this), this._tooltipCallback.bind(this)); const newLinkProvider = this._xterm.registerLinkProvider(wrappedLinkProvider); // Re-register the standard link providers so they are a lower priority that the new one this._registerStandardLinkProviders(); @@ -173,38 +155,11 @@ export class TerminalLinkManager extends DisposableStore { return; } - // Allow the link to be intercepted if there are listeners - if (this._hasBeforeHandleLinkListeners) { - const wasHandled = await this._triggerBeforeHandleLinkListeners(link); - if (!wasHandled) { - handler(event, link); - } - return; - } - // Just call the handler if there is no before listener handler(event, link); }; } - private async _triggerBeforeHandleLinkListeners(link: string): Promise { - return new Promise(r => { - const timeoutId = setTimeout(() => { - canceled = true; - this._logService.error(`An extension intecepted a terminal link but it timed out after ${TerminalLinkManager.LINK_INTERCEPT_THRESHOLD / 1000} seconds`); - r(false); - }, TerminalLinkManager.LINK_INTERCEPT_THRESHOLD); - let canceled = false; - const resolve = (handled: boolean) => { - if (!canceled) { - clearTimeout(timeoutId); - r(handled); - } - }; - this._onBeforeHandleLink.fire({ link, resolve }); - }); - } - protected get _localLinkRegex(): RegExp { if (!this._processManager) { throw new Error('Process manager is required'); @@ -369,7 +324,6 @@ export class TerminalLinkManager extends DisposableStore { * @param link Url link which may contain line and column number. */ public extractLineColumnInfo(link: string): LineColumnInfo { - const matches: string[] | null = this._localLinkRegex.exec(link); const lineColumnInfo: LineColumnInfo = { lineNumber: 1, diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 39098571234..81c21a43511 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -136,14 +136,6 @@ export interface ITerminalService { findNext(): void; findPrevious(): void; - /** - * Link handlers can be registered here to allow intercepting links clicked in the terminal. - * When a link is clicked, the link will be considered handled when the first interceptor - * resolves with true. It will be considered not handled when _all_ link handlers resolve with - * false, or 3 seconds have elapsed. - */ - addLinkHandler(key: string, callback: TerminalLinkHandlerCallback): IDisposable; - /** * Registers a link provider that enables integrators to add links to the terminal. * @param linkProvider When registered, the link provider is asked whenever a cell is hovered @@ -215,8 +207,6 @@ export enum WindowsShellType { } export type TerminalShellType = WindowsShellType | undefined; -export const LINK_INTERCEPT_THRESHOLD = 3000; - export interface ITerminalBeforeHandleLinkEvent { terminal?: ITerminalInstance; /** The text of the link */ @@ -225,8 +215,6 @@ export interface ITerminalBeforeHandleLinkEvent { resolve(wasHandled: boolean): void; } -export type TerminalLinkHandlerCallback = (e: ITerminalBeforeHandleLinkEvent) => Promise; - export interface ITerminalInstance { /** * The ID of the terminal instance, this is an arbitrary number only used to identify the @@ -289,11 +277,6 @@ export interface ITerminalInstance { */ onExit: Event; - /** - * Attach a listener to intercept and handle link clicks in the terminal. - */ - onBeforeHandleLink: Event; - readonly exitCode: number | undefined; readonly areLinksReady: boolean; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 6ca70138147..ee16eb81e97 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -30,7 +30,7 @@ import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGR import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { TerminalLinkManager } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; -import { ITerminalInstanceService, ITerminalInstance, TerminalShellType, WindowsShellType, ITerminalBeforeHandleLinkEvent, ITerminalExternalLinkProvider } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalInstanceService, ITerminalInstance, TerminalShellType, WindowsShellType, ITerminalExternalLinkProvider } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/browser/terminalProcessManager'; import { Terminal as XTermTerminal, IBuffer, ITerminalAddon } from 'xterm'; import { SearchAddon, ISearchOptions } from 'xterm-addon-search'; @@ -164,8 +164,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { public get onMaximumDimensionsChanged(): Event { return this._onMaximumDimensionsChanged.event; } private readonly _onFocus = new Emitter(); public get onFocus(): Event { return this._onFocus.event; } - private readonly _onBeforeHandleLink = new Emitter(); - public get onBeforeHandleLink(): Event { return this._onBeforeHandleLink.event; } public constructor( private readonly _terminalFocusContextKey: IContextKey, @@ -419,10 +417,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { }); } this._linkManager = this._instantiationService.createInstance(TerminalLinkManager, xterm, this._processManager!); - this._linkManager.onBeforeHandleLink(e => { - e.terminal = this; - this._onBeforeHandleLink.fire(e); - }); this._areLinksReady = true; this._onLinksReady.fire(this); }); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index cb0c8c2fca0..2fcce1787c2 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -14,7 +14,7 @@ import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab'; import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; -import { ITerminalService, ITerminalInstance, ITerminalTab, TerminalShellType, WindowsShellType, TerminalLinkHandlerCallback, LINK_INTERCEPT_THRESHOLD, ITerminalExternalLinkProvider } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalService, ITerminalInstance, ITerminalTab, TerminalShellType, WindowsShellType, ITerminalExternalLinkProvider } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { IQuickInputService, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput'; @@ -50,7 +50,6 @@ export class TerminalService implements ITerminalService { private _findState: FindReplaceState; private _extHostsReady: { [authority: string]: IExtHostReadyEntry | undefined } = {}; private _activeTabIndex: number; - private _linkHandlers: { [key: string]: TerminalLinkHandlerCallback } = {}; private _linkProviders: Set = new Set(); private _linkProviderDisposables: Map = new Map(); @@ -428,50 +427,6 @@ export class TerminalService implements ITerminalService { instance.addDisposable(instance.onDimensionsChanged(() => this._onInstanceDimensionsChanged.fire(instance))); instance.addDisposable(instance.onMaximumDimensionsChanged(() => this._onInstanceMaximumDimensionsChanged.fire(instance))); instance.addDisposable(instance.onFocus(this._onActiveInstanceChanged.fire, this._onActiveInstanceChanged)); - instance.addDisposable(instance.onBeforeHandleLink(async e => { - // No link handlers have been registered - const keys = Object.keys(this._linkHandlers); - if (keys.length === 0) { - e.resolve(false); - return; - } - - // Fire each link interceptor and wait for either a true, all false or the cancel time - let resolved = false; - const promises: Promise[] = []; - const timeout = setTimeout(() => { - resolved = true; - e.resolve(false); - }, LINK_INTERCEPT_THRESHOLD); - for (let i = 0; i < keys.length; i++) { - const p = this._linkHandlers[keys[i]](e); - p.then(handled => { - if (!resolved && handled) { - resolved = true; - clearTimeout(timeout); - e.resolve(true); - } - }); - promises.push(p); - } - await Promise.all(promises); - if (!resolved) { - resolved = true; - clearTimeout(timeout); - e.resolve(false); - } - })); - } - - public addLinkHandler(key: string, callback: TerminalLinkHandlerCallback): IDisposable { - this._linkHandlers[key] = callback; - return { - dispose: () => { - if (this._linkHandlers[key] === callback) { - delete this._linkHandlers[key]; - } - } - }; } public registerLinkProvider(linkProvider: ITerminalExternalLinkProvider): IDisposable { diff --git a/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkManager.test.ts b/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkManager.test.ts deleted file mode 100644 index 2223e0d082a..00000000000 --- a/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkManager.test.ts +++ /dev/null @@ -1,163 +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 assert from 'assert'; -import { OperatingSystem } from 'vs/base/common/platform'; -import { TerminalLinkManager, XtermLinkMatcherHandler } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager'; -import { Terminal as XtermTerminal } from 'xterm'; -import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { Event } from 'vs/base/common/event'; -import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { TestPathService, TestEnvironmentService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { IPathService } from 'vs/workbench/services/path/common/pathService'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; - -class TestTerminalLinkManager extends TerminalLinkManager { - public get localLinkRegex(): RegExp { - return this._localLinkRegex; - } - public preprocessPath(link: string): string | null { - return this._preprocessPath(link); - } - protected _isLinkActivationModifierDown(event: MouseEvent): boolean { - return true; - } - public wrapLinkHandler(handler: (link: string) => void): XtermLinkMatcherHandler { - TerminalLinkManager._LINK_INTERCEPT_THRESHOLD = 0; - return this._wrapLinkHandler((_, link) => handler(link)); - } -} - -class MockTerminalInstanceService implements ITerminalInstanceService { - onRequestDefaultShellAndArgs?: Event | undefined; - getDefaultShellAndArgs(): Promise<{ shell: string; args: string | string[] | undefined; }> { - throw new Error('Method not implemented.'); - } - declare readonly _serviceBrand: undefined; - getXtermConstructor(): Promise { - throw new Error('Method not implemented.'); - } - getXtermSearchConstructor(): Promise { - throw new Error('Method not implemented.'); - } - getXtermUnicode11Constructor(): Promise { - throw new Error('Method not implemented.'); - } - getXtermWebglConstructor(): Promise { - throw new Error('Method not implemented.'); - } - createWindowsShellHelper(): any { - throw new Error('Method not implemented.'); - } - createTerminalProcess(): any { - throw new Error('Method not implemented.'); - } - getMainProcessParentEnv(): any { - throw new Error('Method not implemented.'); - } -} - -suite('Workbench - TerminalLinkManager', () => { - let instantiationService: TestInstantiationService; - - setup(async () => { - const configurationService = new TestConfigurationService(); - await configurationService.setUserConfiguration('terminal', { integrated: { enableFileLinks: true } }); - - instantiationService = new TestInstantiationService(); - instantiationService.stub(IEnvironmentService, TestEnvironmentService); - instantiationService.stub(IPathService, new TestPathService()); - instantiationService.stub(ITerminalInstanceService, new MockTerminalInstanceService()); - instantiationService.stub(IConfigurationService, configurationService); - }); - - suite('preprocessPath', () => { - test('Windows', () => { - const linkHandler: TestTerminalLinkManager = instantiationService.createInstance(TestTerminalLinkManager, new XtermTerminal() as any, { - os: OperatingSystem.Windows, - userHome: 'C:\\Users\\Me' - } as any); - linkHandler.processCwd = 'C:\\base'; - - assert.equal(linkHandler.preprocessPath('./src/file1'), 'C:\\base\\src\\file1'); - assert.equal(linkHandler.preprocessPath('src\\file2'), 'C:\\base\\src\\file2'); - assert.equal(linkHandler.preprocessPath('~/src/file3'), 'C:\\Users\\Me\\src\\file3'); - assert.equal(linkHandler.preprocessPath('~\\src\\file4'), 'C:\\Users\\Me\\src\\file4'); - assert.equal(linkHandler.preprocessPath('C:\\absolute\\path\\file5'), 'C:\\absolute\\path\\file5'); - assert.equal(linkHandler.preprocessPath('\\\\?\\C:\\absolute\\path\\extended\\file6'), 'C:\\absolute\\path\\extended\\file6'); - }); - test('Windows - spaces', () => { - const linkHandler: TestTerminalLinkManager = instantiationService.createInstance(TestTerminalLinkManager, new XtermTerminal() as any, { - os: OperatingSystem.Windows, - userHome: 'C:\\Users\\M e' - } as any); - linkHandler.processCwd = 'C:\\base dir'; - - assert.equal(linkHandler.preprocessPath('./src/file1'), 'C:\\base dir\\src\\file1'); - assert.equal(linkHandler.preprocessPath('src\\file2'), 'C:\\base dir\\src\\file2'); - assert.equal(linkHandler.preprocessPath('~/src/file3'), 'C:\\Users\\M e\\src\\file3'); - assert.equal(linkHandler.preprocessPath('~\\src\\file4'), 'C:\\Users\\M e\\src\\file4'); - assert.equal(linkHandler.preprocessPath('C:\\abso lute\\path\\file5'), 'C:\\abso lute\\path\\file5'); - }); - - test('Linux', () => { - const linkHandler: TestTerminalLinkManager = instantiationService.createInstance(TestTerminalLinkManager, new XtermTerminal() as any, { - os: OperatingSystem.Linux, - userHome: '/home/me' - } as any); - linkHandler.processCwd = '/base'; - - assert.equal(linkHandler.preprocessPath('./src/file1'), '/base/src/file1'); - assert.equal(linkHandler.preprocessPath('src/file2'), '/base/src/file2'); - assert.equal(linkHandler.preprocessPath('~/src/file3'), '/home/me/src/file3'); - assert.equal(linkHandler.preprocessPath('/absolute/path/file4'), '/absolute/path/file4'); - }); - - test('No Workspace', () => { - const linkHandler: TestTerminalLinkManager = instantiationService.createInstance(TestTerminalLinkManager, new XtermTerminal() as any, { - os: OperatingSystem.Linux, - userHome: '/home/me' - } as any); - - assert.equal(linkHandler.preprocessPath('./src/file1'), null); - assert.equal(linkHandler.preprocessPath('src/file2'), null); - assert.equal(linkHandler.preprocessPath('~/src/file3'), '/home/me/src/file3'); - assert.equal(linkHandler.preprocessPath('/absolute/path/file4'), '/absolute/path/file4'); - }); - }); - - suite('wrapLinkHandler', () => { - const nullMouseEvent: any = Object.freeze({ preventDefault: () => { } }); - - test('should allow intercepting of links with onBeforeHandleLink', async () => { - const linkHandler: TestTerminalLinkManager = instantiationService.createInstance(TestTerminalLinkManager, new XtermTerminal() as any, { - os: OperatingSystem.Linux, - userHome: '' - } as any); - linkHandler.onBeforeHandleLink(e => { - if (e.link === 'https://www.microsoft.com') { - intercepted = true; - e.resolve(true); - } - e.resolve(false); - }); - const wrappedHandler = linkHandler.wrapLinkHandler(() => defaultHandled = true); - - let defaultHandled = false; - let intercepted = false; - await wrappedHandler(nullMouseEvent, 'https://www.visualstudio.com'); - assert.equal(intercepted, false); - assert.equal(defaultHandled, true); - - defaultHandled = false; - intercepted = false; - await wrappedHandler(nullMouseEvent, 'https://www.microsoft.com'); - assert.equal(intercepted, true); - assert.equal(defaultHandled, false); - }); - }); -}); From 03467e96ff0d8a00f0ae90017e7f3f14bda27126 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 10 Aug 2020 14:19:22 -0700 Subject: [PATCH 080/736] re #104262. Add logging for notebook. --- src/vs/workbench/api/browser/mainThreadNotebook.ts | 8 +++++++- src/vs/workbench/api/common/extHost.api.impl.ts | 2 +- src/vs/workbench/api/common/extHostNotebook.ts | 4 ++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 9f627eb2cf4..ba8c89315d1 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -21,6 +21,7 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { Emitter } from 'vs/base/common/event'; +import { ILogService } from 'vs/platform/log/common/log'; export class MainThreadNotebookDocument extends Disposable { private _textModel: NotebookTextModel; @@ -181,7 +182,8 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo @INotebookService private _notebookService: INotebookService, @IConfigurationService private readonly configurationService: IConfigurationService, @IEditorService private readonly editorService: IEditorService, - @IAccessibilityService private readonly accessibilityService: IAccessibilityService + @IAccessibilityService private readonly accessibilityService: IAccessibilityService, + @ILogService private readonly logService: ILogService ) { super(); @@ -582,21 +584,25 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo } async $updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise { + this.logService.debug('MainThreadNotebooks#updateNotebookLanguages', resource.path, languages); const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); textModel?.updateLanguages(languages); } async $updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata): Promise { + this.logService.debug('MainThreadNotebooks#updateNotebookMetadata', resource.path, metadata); const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); textModel?.updateNotebookMetadata(metadata); } async $updateNotebookCellMetadata(viewType: string, resource: UriComponents, handle: number, metadata: NotebookCellMetadata): Promise { + this.logService.debug('MainThreadNotebooks#updateNotebookCellMetadata', resource.path, handle, metadata); const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); textModel?.updateNotebookCellMetadata(handle, metadata); } async $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): Promise { + this.logService.debug('MainThreadNotebooks#spliceNotebookCellOutputs', resource.path, cellHandle); const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); if (textModel) { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 77f89a398a7..da4884a3b76 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -137,7 +137,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostComment = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostCommands, extHostDocuments)); const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress))); const extHostLabelService = rpcProtocol.set(ExtHostContext.ExtHosLabelService, new ExtHostLabelService(rpcProtocol)); - const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, initData.uiKind === UIKind.Web ? new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, initData.environment) : new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, initData.environment, extensionStoragePaths)); + const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, initData.uiKind === UIKind.Web ? new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, initData.environment, extHostLogService) : new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, initData.environment, extHostLogService, extensionStoragePaths)); const extHostTheming = rpcProtocol.set(ExtHostContext.ExtHostTheming, new ExtHostTheming(rpcProtocol)); const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol)); const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol, extHostCommands)); diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 9d9c567c468..e83ba02730b 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -15,6 +15,7 @@ import { NotImplementedProxy } from 'vs/base/common/types'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as UUID from 'vs/base/common/uuid'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ILogService } from 'vs/platform/log/common/log'; import { CellKind, ExtHostNotebookShape, IMainContext, INotebookDocumentsAndEditorsDelta, INotebookEditorPropertiesChangeData, MainContext, MainThreadDocumentsShape, MainThreadNotebookShape, NotebookCellOutputsSplice } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData'; @@ -943,6 +944,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN commands: ExtHostCommands, private _documentsAndEditors: ExtHostDocumentsAndEditors, private readonly _webviewInitData: WebviewInitData, + private readonly logService: ILogService, private readonly _extensionStoragePaths?: IExtensionStoragePaths, ) { this._proxy = mainContext.getProxy(MainContext.MainThreadNotebook); @@ -1445,6 +1447,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } $acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent): void { + const document = this._documents.get(URI.revive(uriComponents).toString()); if (document) { @@ -1461,6 +1464,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } $acceptEditorPropertiesChanged(uriComponents: UriComponents, data: INotebookEditorPropertiesChangeData): void { + this.logService.debug('ExtHostNotebook#$acceptEditorPropertiesChanged', uriComponents.path, data); const editor = this._getEditorFromURI(uriComponents); if (!editor) { From 391bca591eda22ce9daead3ca9e8838715335891 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 10 Aug 2020 14:30:19 -0700 Subject: [PATCH 081/736] re #104262. add log for execution and cancel. --- .../api/browser/mainThreadNotebook.ts | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index ba8c89315d1..ceed17dc7b2 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -474,10 +474,10 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo await this._proxy.$resolveNotebookEditor(viewType, uri, editorId); }, executeNotebookByAttachedKernel: async (viewType: string, uri: URI) => { - return this.executeNotebookByAttachedKernel(viewType, uri); + return this.executeNotebookByAttachedKernel(viewType, uri, undefined); }, cancelNotebookByAttachedKernel: async (viewType: string, uri: URI) => { - return this.cancelNotebookByAttachedKernel(viewType, uri); + return this.cancelNotebookByAttachedKernel(viewType, uri, undefined); }, onDidReceiveMessage: (editorId: string, rendererType: string | undefined, message: unknown) => { this._proxy.$onDidReceiveMessage(editorId, rendererType, message); @@ -486,10 +486,10 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo return this.removeNotebookTextModel(uri); }, executeNotebookCell: async (uri: URI, handle: number) => { - return this._proxy.$executeNotebookByAttachedKernel(_viewType, uri, handle); + return this.executeNotebookByAttachedKernel(_viewType, uri, handle); }, cancelNotebookCell: async (uri: URI, handle: number) => { - return this._proxy.$cancelNotebookByAttachedKernel(_viewType, uri, handle); + return this.cancelNotebookByAttachedKernel(_viewType, uri, handle); }, save: async (uri: URI, token: CancellationToken) => { return this._proxy.$saveNotebook(_viewType, uri, token); @@ -519,7 +519,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo } async $registerNotebookKernel(extension: NotebookExtensionDescription, id: string, label: string, selectors: (string | IRelativePattern)[], preloads: UriComponents[]): Promise { - const kernel = new MainThreadNotebookKernel(this._proxy, id, label, selectors, extension.id, URI.revive(extension.location), preloads.map(preload => URI.revive(preload))); + const kernel = new MainThreadNotebookKernel(this._proxy, id, label, selectors, extension.id, URI.revive(extension.location), preloads.map(preload => URI.revive(preload)), this.logService); this._notebookKernels.set(id, kernel); this._notebookService.registerNotebookKernel(kernel); return; @@ -552,9 +552,11 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo return that._proxy.$resolveNotebookKernel(handle, editorId, uri, kernelId, token); }, executeNotebook: (uri: URI, kernelId: string, cellHandle: number | undefined) => { + this.logService.debug('MainthreadNotebooks.registerNotebookKernelProvider#executeNotebook', uri.path, kernelId, cellHandle); return that._proxy.$executeNotebookKernelFromProvider(handle, uri, kernelId, cellHandle); }, cancelNotebook: (uri: URI, kernelId: string, cellHandle: number | undefined) => { + this.logService.debug('MainthreadNotebooks.registerNotebookKernelProvider#cancelNotebook', uri.path, kernelId, cellHandle); return that._proxy.$cancelNotebookKernelFromProvider(handle, uri, kernelId, cellHandle); }, }); @@ -611,12 +613,14 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo } } - async executeNotebookByAttachedKernel(viewType: string, uri: URI): Promise { - return this._proxy.$executeNotebookByAttachedKernel(viewType, uri, undefined); + async executeNotebookByAttachedKernel(viewType: string, uri: URI, handle: number | undefined): Promise { + this.logService.debug('MainthreadNotebooks#executeNotebookByAttachedKernel', uri.path, handle); + return this._proxy.$executeNotebookByAttachedKernel(viewType, uri, handle); } - async cancelNotebookByAttachedKernel(viewType: string, uri: URI): Promise { - return this._proxy.$cancelNotebookByAttachedKernel(viewType, uri, undefined); + async cancelNotebookByAttachedKernel(viewType: string, uri: URI, handle: number | undefined): Promise { + this.logService.debug('MainthreadNotebooks#cancelNotebookByAttachedKernel', uri.path, handle); + return this._proxy.$cancelNotebookByAttachedKernel(viewType, uri, handle); } async $postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise { @@ -655,11 +659,13 @@ export class MainThreadNotebookKernel implements INotebookKernelInfo { readonly selectors: (string | IRelativePattern)[], readonly extension: ExtensionIdentifier, readonly extensionLocation: URI, - readonly preloads: URI[] + readonly preloads: URI[], + readonly logService: ILogService ) { } async executeNotebook(viewType: string, uri: URI, handle: number | undefined): Promise { + this.logService.debug('MainThreadNotebookKernel#executeNotebook', uri.path, handle); return this._proxy.$executeNotebook2(this.id, viewType, uri, handle); } } From d21ff5b1c318622613be170ac050bd5a87804b03 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 10 Aug 2020 14:30:46 -0700 Subject: [PATCH 082/736] Remove expect error for TS 4.0 --- .../src/languageFeatures/completions.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/completions.ts b/extensions/typescript-language-features/src/languageFeatures/completions.ts index ae827b95b14..b062d8aa854 100644 --- a/extensions/typescript-language-features/src/languageFeatures/completions.ts +++ b/extensions/typescript-language-features/src/languageFeatures/completions.ts @@ -345,7 +345,6 @@ class CompletionAcceptedCommand implements Command { } */ this.telemetryReporter.logTelemetry('completions.accept', { - // @ts-expect-error - remove after TS 4.0 protocol update isPackageJsonImport: item.tsEntry.isPackageJsonImport ? 'true' : undefined, }); } @@ -540,7 +539,6 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider< for (let entry of entries) { if (!shouldExcludeCompletionEntry(entry, completionConfiguration)) { items.push(new MyCompletionItem(position, document, entry, completionContext, metadata)); - // @ts-expect-error - remove after TS 4.0 protocol update includesPackageJsonImport = !!entry.isPackageJsonImport; } } From a3e37dc103215e6d8060a439843d966312abb705 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 10 Aug 2020 14:33:10 -0700 Subject: [PATCH 083/736] Remove expect error --- .../src/typescriptServiceClient.ts | 1 - .../typescript-language-features/src/utils/configuration.ts | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 69a3c049040..4d5886049d0 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -532,7 +532,6 @@ export default class TypeScriptServiceClient extends Disposable implements IType preferences: { providePrefixAndSuffixTextForRename: true, allowRenameOfImportPath: true, - // @ts-expect-error, remove after 4.0 protocol update includePackageJsonAutoImports: this._configuration.includePackageJsonAutoImports, }, watchOptions diff --git a/extensions/typescript-language-features/src/utils/configuration.ts b/extensions/typescript-language-features/src/utils/configuration.ts index cde1e14b9a0..f549ff6f9e2 100644 --- a/extensions/typescript-language-features/src/utils/configuration.ts +++ b/extensions/typescript-language-features/src/utils/configuration.ts @@ -66,7 +66,7 @@ export class TypeScriptServiceConfiguration { public readonly maxTsServerMemory: number; public readonly enablePromptUseWorkspaceTsdk: boolean; public readonly watchOptions: protocol.WatchOptions | undefined; - public readonly includePackageJsonAutoImports: string | undefined; + public readonly includePackageJsonAutoImports: 'auto' | 'on' | 'off' | undefined; public static loadFromWorkspace(): TypeScriptServiceConfiguration { return new TypeScriptServiceConfiguration(); @@ -181,8 +181,8 @@ export class TypeScriptServiceConfiguration { return configuration.get('typescript.tsserver.watchOptions'); } - private static readIncludePackageJsonAutoImports(configuration: vscode.WorkspaceConfiguration): string | undefined { - return configuration.get('typescript.preferences.includePackageJsonAutoImports'); + private static readIncludePackageJsonAutoImports(configuration: vscode.WorkspaceConfiguration): 'auto' | 'on' | 'off' | undefined { + return configuration.get<'auto' | 'on' | 'off'>('typescript.preferences.includePackageJsonAutoImports'); } private static readMaxTsServerMemory(configuration: vscode.WorkspaceConfiguration): number { From c539d21915d729ff8f5e008e629f9bfabb30b621 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 10 Aug 2020 15:02:43 -0700 Subject: [PATCH 084/736] Protect against exception from line not existing Fixes #102337 --- .../contrib/terminal/browser/links/terminalLinkHelpers.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers.ts index de8cb63d107..9297f97fd2a 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers.ts @@ -21,10 +21,16 @@ export function convertLinkRangeToBuffer(lines: IBufferLine[], bufferWidth: numb // Shift start range right for each wide character before the link let startOffset = 0; const startWrappedLineCount = Math.ceil(range.startColumn / bufferWidth); - for (let y = 0; y < startWrappedLineCount; y++) { + for (let y = 0; y < Math.min(startWrappedLineCount); y++) { const lineLength = Math.min(bufferWidth, range.startColumn - y * bufferWidth); let lineOffset = 0; const line = lines[y]; + // Sanity check for line, apparently this can happen but it's not clear under what + // circumstances this happens. Continue on, skipping the remainder of start offset if this + // happens to minimize impact. + if (!line) { + break; + } for (let x = 0; x < Math.min(bufferWidth, lineLength + lineOffset); x++) { const cell = line.getCell(x)!; const width = cell.getWidth(); From e4217281505b44726e6d56f51a94ff1af045f9fe Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Mon, 10 Aug 2020 15:16:51 -0700 Subject: [PATCH 085/736] Add limitHit messaging to search editor. --- .../contrib/searchEditor/browser/searchEditor.ts | 12 ++++++++++-- .../browser/searchEditorSerialization.ts | 8 ++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts index ae1538b3ec8..533d6fe56fb 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts @@ -82,6 +82,7 @@ export class SearchEditor extends BaseTextEditor { private messageDisposables: IDisposable[] = []; private container: HTMLElement; private searchModel: SearchModel; + private ongoingOperations: number = 0; constructor( @ITelemetryService telemetryService: ITelemetryService, @@ -493,7 +494,14 @@ export class SearchEditor extends BaseTextEditor { } this.searchOperation.start(500); - await this.searchModel.search(query).finally(() => this.searchOperation.stop()); + this.ongoingOperations++; + const exit = await this.searchModel.search(query).finally(() => { + this.ongoingOperations--; + if (this.ongoingOperations === 0) { + this.searchOperation.stop(); + } + }); + const input = this.getInput(); if (!input || input !== startInput || @@ -505,7 +513,7 @@ export class SearchEditor extends BaseTextEditor { const controller = ReferencesController.get(this.searchResultEditor); controller.closeWidget(false); const labelFormatter = (uri: URI): string => this.labelService.getUriLabel(uri, { relative: true }); - const results = serializeSearchResultForEditor(this.searchModel.searchResult, config.includes, config.excludes, config.contextLines, labelFormatter, sortOrder); + const results = serializeSearchResultForEditor(this.searchModel.searchResult, config.includes, config.excludes, config.contextLines, labelFormatter, sortOrder, exit?.limitHit); const { body } = await input.getModels(); this.modelService.updateModel(body, results.text); input.config = config; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts index 1bb2abc19ab..31d48055864 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts @@ -208,7 +208,7 @@ export const extractSearchQueryFromLines = (lines: string[]): SearchConfiguratio }; export const serializeSearchResultForEditor = - (searchResult: SearchResult, rawIncludePattern: string, rawExcludePattern: string, contextLines: number, labelFormatter: (x: URI) => string, sortOrder: SearchSortOrder): { matchRanges: Range[], text: string, config: Partial } => { + (searchResult: SearchResult, rawIncludePattern: string, rawExcludePattern: string, contextLines: number, labelFormatter: (x: URI) => string, sortOrder: SearchSortOrder, limitHit?: boolean): { matchRanges: Range[], text: string, config: Partial } => { if (!searchResult.query) { throw Error('Internal Error: Expected query, got null'); } const config = contentPatternToSearchConfiguration(searchResult.query, rawIncludePattern, rawExcludePattern, contextLines); @@ -219,7 +219,11 @@ export const serializeSearchResultForEditor = searchResult.count() ? `${resultcount} - ${filecount}` : localize('noResults', "No Results"), - '']; + ]; + if (limitHit) { + info.push(localize('searchMaxResultsWarning', "The result set only contains a subset of all matches. Please be more specific in your search to narrow down the results.")); + } + info.push(''); const matchComparer = (a: FileMatch | FolderMatch, b: FileMatch | FolderMatch) => searchMatchComparer(a, b, sortOrder); From 85b70cc872cd970b6308770c61744dd7c3175170 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 10 Aug 2020 16:24:39 -0700 Subject: [PATCH 086/736] npm: enable debugging all scripts Fixes https://github.com/microsoft/vscode/issues/104403 Fixes https://github.com/microsoft/vscode/issues/102847 --- extensions/npm/package.json | 7 +----- extensions/npm/src/npmView.ts | 41 ++++++++----------------------- extensions/npm/src/scriptHover.ts | 18 ++++---------- extensions/npm/src/tasks.ts | 36 ++++----------------------- 4 files changed, 21 insertions(+), 81 deletions(-) diff --git a/extensions/npm/package.json b/extensions/npm/package.json index a77b48647cd..4dd9ee6a71c 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -166,14 +166,9 @@ "when": "view == npm && viewItem == script", "group": "inline" }, - { - "command": "npm.runScript", - "when": "view == npm && viewItem == debugScript", - "group": "inline" - }, { "command": "npm.debugScript", - "when": "view == npm && viewItem == debugScript", + "when": "view == npm && viewItem == script", "group": "inline" }, { diff --git a/extensions/npm/src/npmView.ts b/extensions/npm/src/npmView.ts index 0a4908ec440..72ec421f775 100644 --- a/extensions/npm/src/npmView.ts +++ b/extensions/npm/src/npmView.ts @@ -3,18 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { JSONVisitor, visit } from 'jsonc-parser'; import * as path from 'path'; import { - Event, EventEmitter, ExtensionContext, Task2 as Task, - TextDocument, ThemeIcon, TreeDataProvider, TreeItem, TreeItemCollapsibleState, Uri, - WorkspaceFolder, commands, window, workspace, tasks, Selection, TaskGroup + commands, Event, EventEmitter, ExtensionContext, + Selection, Task2 as Task, + TaskGroup, tasks, TextDocument, ThemeIcon, TreeDataProvider, TreeItem, TreeItemCollapsibleState, Uri, + window, workspace, WorkspaceFolder } from 'vscode'; -import { visit, JSONVisitor } from 'jsonc-parser'; -import { - NpmTaskDefinition, getPackageJsonUriFromTask, getScripts, - isWorkspaceFolder, getTaskName, createTask, extractDebugArgFromScript, startDebugging, isAutoDetectionEnabled -} from './tasks'; import * as nls from 'vscode-nls'; +import { + createTask, getTaskName, isAutoDetectionEnabled, isWorkspaceFolder, NpmTaskDefinition, + startDebugging +} from './tasks'; const localize = nls.loadMessageBundle(); @@ -90,9 +91,6 @@ class NpmScript extends TreeItem { } }; this.contextValue = 'script'; - if (task.group && task.group === TaskGroup.Rebuild) { - this.contextValue = 'debugScript'; - } this.package = packageJson; this.task = task; this.command = commandList[command]; @@ -139,27 +137,8 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { tasks.executeTask(script.task); } - private extractDebugArg(scripts: any, task: Task): [string, number] | undefined { - return extractDebugArgFromScript(scripts[task.name]); - } - private async debugScript(script: NpmScript) { - let task = script.task; - let uri = getPackageJsonUriFromTask(task); - let scripts = await getScripts(uri!); - - let debugArg = this.extractDebugArg(scripts, task); - if (!debugArg) { - let message = localize('noDebugOptions', 'Could not launch "{0}" for debugging because the scripts lacks a node debug option, e.g. "--inspect-brk".', task.name); - let learnMore = localize('learnMore', 'Learn More'); - let ok = localize('ok', 'OK'); - let result = await window.showErrorMessage(message, { modal: true }, ok, learnMore); - if (result === learnMore) { - commands.executeCommand('vscode.open', Uri.parse('https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_launch-configuration-support-for-npm-and-other-tools')); - } - return; - } - startDebugging(task.name, debugArg[0], debugArg[1], script.getFolder()); + startDebugging(script.task.name, script.getFolder()); } private findScript(document: TextDocument, script?: NpmScript): number { diff --git a/extensions/npm/src/scriptHover.ts b/extensions/npm/src/scriptHover.ts index aa803dbc1d4..f8a5482bef8 100644 --- a/extensions/npm/src/scriptHover.ts +++ b/extensions/npm/src/scriptHover.ts @@ -8,7 +8,7 @@ import { workspace, tasks, Range, HoverProvider, Hover, Position, MarkdownString, Uri } from 'vscode'; import { - createTask, startDebugging, findAllScriptRanges, extractDebugArgFromScript + createTask, startDebugging, findAllScriptRanges } from './tasks'; import * as nls from 'vscode-nls'; @@ -54,11 +54,7 @@ export class NpmScriptHoverProvider implements HoverProvider { let contents: MarkdownString = new MarkdownString(); contents.isTrusted = true; contents.appendMarkdown(this.createRunScriptMarkdown(key, document.uri)); - - let debugArgs = extractDebugArgFromScript(value[2]); - if (debugArgs) { - contents.appendMarkdown(this.createDebugScriptMarkdown(key, document.uri, debugArgs[0], debugArgs[1])); - } + contents.appendMarkdown(this.createDebugScriptMarkdown(key, document.uri)); hover = new Hover(contents); } }); @@ -78,12 +74,10 @@ export class NpmScriptHoverProvider implements HoverProvider { ); } - private createDebugScriptMarkdown(script: string, documentUri: Uri, protocol: string, port: number): string { - let args = { + private createDebugScriptMarkdown(script: string, documentUri: Uri): string { + const args = { documentUri: documentUri, script: script, - protocol: protocol, - port: port }; return this.createMarkdownLink( localize('debugScript', 'Debug Script'), @@ -116,11 +110,9 @@ export class NpmScriptHoverProvider implements HoverProvider { public debugScriptFromHover(args: any) { let script = args.script; let documentUri = args.documentUri; - let protocol = args.protocol; - let port = args.port; let folder = workspace.getWorkspaceFolder(documentUri); if (folder) { - startDebugging(script, protocol, port, folder); + startDebugging(script, folder); } } } diff --git a/extensions/npm/src/tasks.ts b/extensions/npm/src/tasks.ts index 2f4947de447..040e1f820cc 100644 --- a/extensions/npm/src/tasks.ts +++ b/extensions/npm/src/tasks.ts @@ -249,6 +249,8 @@ async function provideNpmScriptsForFolder(packageJsonUri: Uri): Promise if (prePostScripts.has(each)) { task.group = TaskGroup.Clean; // hack: use Clean group to tag pre/post scripts } + + // todo@connor4312: all scripts are now debuggable, what is a 'debug script'? if (isDebugScript(scripts![each])) { task.group = TaskGroup.Rebuild; // hack: use Rebuild group to tag debug scripts } @@ -355,44 +357,16 @@ export function runScript(script: string, document: TextDocument) { } } -export function extractDebugArgFromScript(scriptValue: string): [string, number] | undefined { - // matches --debug, --debug=1234, --debug-brk, debug-brk=1234, --inspect, - // --inspect=1234, --inspect-brk, --inspect-brk=1234, - // --inspect=localhost:1245, --inspect=127.0.0.1:1234, --inspect=[aa:1:0:0:0]:1234, --inspect=:1234 - let match = scriptValue.match(/--(inspect|debug)(-brk)?(=((\[[0-9a-fA-F:]*\]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[a-zA-Z0-9\.]*):)?(\d+))?/); - - if (match) { - if (match[6]) { - return [match[1], parseInt(match[6])]; - } - if (match[1] === 'inspect') { - return [match[1], 9229]; - } - if (match[1] === 'debug') { - return [match[1], 5858]; - } - } - return undefined; -} - -export function startDebugging(scriptName: string, protocol: string, port: number, folder: WorkspaceFolder) { - let p = 'inspector'; - if (protocol === 'debug') { - p = 'legacy'; - } - - let packageManager = getPackageManager(folder); +export function startDebugging(scriptName: string, folder: WorkspaceFolder) { const config: DebugConfiguration = { - type: 'node', + type: 'pwa-node', request: 'launch', name: `Debug ${scriptName}`, - runtimeExecutable: packageManager, + runtimeExecutable: getPackageManager(folder), runtimeArgs: [ 'run', scriptName, ], - port: port, - protocol: p }; if (folder) { From 4e18099869e9641dca2974993925aa4d1580bf71 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 10 Aug 2020 16:53:06 -0700 Subject: [PATCH 087/736] fix #104095. Register undo/redo/copy implementation for windows & linux. --- .../electron-browser/notebook.contribution.ts | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/electron-browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/electron-browser/notebook.contribution.ts index fe8d01653ca..7609672b040 100644 --- a/src/vs/workbench/contrib/notebook/electron-browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/electron-browser/notebook.contribution.ts @@ -29,35 +29,34 @@ function getFocusedElectronBasedWebviewDelegate(accessor: ServicesAccessor): Ele return; } -if (isMacintosh) { - function withWebview(accessor: ServicesAccessor, f: (webviewe: ElectronWebviewBasedWebview) => void) { - const webview = getFocusedElectronBasedWebviewDelegate(accessor); - if (webview) { - f(webview); - return true; - } - return false; +function withWebview(accessor: ServicesAccessor, f: (webviewe: ElectronWebviewBasedWebview) => void) { + const webview = getFocusedElectronBasedWebviewDelegate(accessor); + if (webview) { + f(webview); + return true; } - - const PRIORITY = 100; - - UndoCommand.addImplementation(PRIORITY, accessor => { - return withWebview(accessor, webview => webview.undo()); - }); - - RedoCommand.addImplementation(PRIORITY, accessor => { - return withWebview(accessor, webview => webview.redo()); - }); - - CopyAction?.addImplementation(PRIORITY, accessor => { - return withWebview(accessor, webview => webview.copy()); - }); - - PasteAction?.addImplementation(PRIORITY, accessor => { - return withWebview(accessor, webview => webview.paste()); - }); - - CutAction?.addImplementation(PRIORITY, accessor => { - return withWebview(accessor, webview => webview.cut()); - }); + return false; } + +const PRIORITY = 100; + +UndoCommand.addImplementation(PRIORITY, accessor => { + return withWebview(accessor, webview => webview.undo()); +}); + +RedoCommand.addImplementation(PRIORITY, accessor => { + return withWebview(accessor, webview => webview.redo()); +}); + +CopyAction?.addImplementation(PRIORITY, accessor => { + return withWebview(accessor, webview => webview.copy()); +}); + +PasteAction?.addImplementation(PRIORITY, accessor => { + return withWebview(accessor, webview => webview.paste()); +}); + +CutAction?.addImplementation(PRIORITY, accessor => { + return withWebview(accessor, webview => webview.cut()); +}); + From 4346fb91d0e6698b98db01dea05152237c6a31ae Mon Sep 17 00:00:00 2001 From: Chuang Yu Date: Tue, 11 Aug 2020 08:12:44 +0800 Subject: [PATCH 088/736] add delay for showing progress --- src/vs/editor/contrib/format/formatActions.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index 0282114d516..8296fe06333 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -233,7 +233,8 @@ class FormatDocumentAction extends EditorAction { const instaService = accessor.get(IInstantiationService); const progressService = accessor.get(IEditorProgressService); await progressService.showWhile( - instaService.invokeFunction(formatDocumentWithSelectedProvider, editor, FormattingMode.Explicit, Progress.None, CancellationToken.None) + instaService.invokeFunction(formatDocumentWithSelectedProvider, editor, FormattingMode.Explicit, Progress.None, CancellationToken.None), + 250 ); } } @@ -273,7 +274,8 @@ class FormatSelectionAction extends EditorAction { const progressService = accessor.get(IEditorProgressService); await progressService.showWhile( - instaService.invokeFunction(formatDocumentRangeWithSelectedProvider, editor, range, FormattingMode.Explicit, CancellationToken.None) + instaService.invokeFunction(formatDocumentRangeWithSelectedProvider, editor, range, FormattingMode.Explicit, CancellationToken.None), + 250 ); } } From ff29bed7a88bdbae7ff7e08f8a0d79804fe28676 Mon Sep 17 00:00:00 2001 From: bamurtaugh Date: Mon, 10 Aug 2020 17:21:23 -0700 Subject: [PATCH 089/736] Update to use commandService --- src/vs/workbench/contrib/debug/browser/debugService.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index dbdaa80335a..5dbe331e2a7 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -121,6 +121,7 @@ export class DebugService implements IDebugService { this.viewModel = new ViewModel(contextKeyService); this.taskRunner = this.instantiationService.createInstance(DebugTaskRunner); + this.commandService = this.instantiationService.createInstance(CommandService); this.toDispose.push(this.fileService.onDidFilesChange(e => this.onFileChanges(e))); this.toDispose.push(this.lifecycleService.onShutdown(this.dispose, this)); @@ -442,11 +443,11 @@ export class DebugService implements IDebugService { nls.localize('installAdditionalDebuggers', "Install {0} Extension", resolvedConfig.type), undefined, true, - async () => { + async () => this.commandService.executeCommand('debug.installAdditionalDebuggers') /*{ const viewlet = (await this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true))?.getViewPaneContainer() as IExtensionsViewPaneContainer; viewlet.search('tag:debuggers @sort:installs'); return this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true); - } + }*/ //() => this.commandService.executeCommand('debug.installAdditionalDebuggers') )); From 6294c6fc6e5737bb27ccf815995a92bb32dc30a8 Mon Sep 17 00:00:00 2001 From: bamurtaugh Date: Mon, 10 Aug 2020 17:24:20 -0700 Subject: [PATCH 090/736] Remove extra code --- src/vs/workbench/contrib/debug/browser/debugService.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 5dbe331e2a7..30e25291a32 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -46,7 +46,6 @@ import { generateUuid } from 'vs/base/common/uuid'; import { DebugStorage } from 'vs/workbench/contrib/debug/common/debugStorage'; import { DebugTelemetry } from 'vs/workbench/contrib/debug/common/debugTelemetry'; import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompoundRoot'; -import { IExtensionsViewPaneContainer, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { CommandService } from 'vs/workbench/services/commands/common/commandService'; export class DebugService implements IDebugService { @@ -443,12 +442,7 @@ export class DebugService implements IDebugService { nls.localize('installAdditionalDebuggers', "Install {0} Extension", resolvedConfig.type), undefined, true, - async () => this.commandService.executeCommand('debug.installAdditionalDebuggers') /*{ - const viewlet = (await this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true))?.getViewPaneContainer() as IExtensionsViewPaneContainer; - viewlet.search('tag:debuggers @sort:installs'); - return this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true); - }*/ - //() => this.commandService.executeCommand('debug.installAdditionalDebuggers') + async () => this.commandService.executeCommand('debug.installAdditionalDebuggers') )); await this.showError(message, actionList); From 5fb45aaf1993cb280465a4f5dcac4436bb2f8fce Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 10 Aug 2020 17:25:32 -0700 Subject: [PATCH 091/736] cursorMove. --- src/vs/editor/common/config/editorOptions.ts | 12 ++++++------ src/vs/editor/contrib/find/findModel.ts | 12 +++--------- src/vs/monaco.d.ts | 2 +- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 1200397a6c8..30004656390 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -1285,7 +1285,7 @@ export interface IEditorFindOptions { /** * Controls whether the cursor should move to find matches while typing. */ - moveOnType?: boolean; + cursorMoveOnType?: boolean; /** * Controls if we seed search string in the Find Widget with editor selection. */ @@ -1315,7 +1315,7 @@ class EditorFind extends BaseEditorOption constructor() { const defaults: EditorFindOptions = { - moveOnType: true, + cursorMoveOnType: true, seedSearchStringFromSelection: true, autoFindInSelection: 'never', globalFindClipboard: false, @@ -1325,10 +1325,10 @@ class EditorFind extends BaseEditorOption super( EditorOption.find, 'find', defaults, { - 'editor.find.moveOnType': { + 'editor.find.cursorMoveOnType': { type: 'boolean', - default: defaults.moveOnType, - description: nls.localize('find.moveOnType', "Controls whether the cursor should jump to find matches while typing.") + default: defaults.cursorMoveOnType, + description: nls.localize('find.cursorMoveOnType', "Controls whether the cursor should jump to find matches while typing.") }, 'editor.find.seedSearchStringFromSelection': { type: 'boolean', @@ -1373,7 +1373,7 @@ class EditorFind extends BaseEditorOption } const input = _input as IEditorFindOptions; return { - moveOnType: EditorBooleanOption.boolean(input.moveOnType, this.defaultValue.moveOnType), + cursorMoveOnType: EditorBooleanOption.boolean(input.cursorMoveOnType, this.defaultValue.cursorMoveOnType), seedSearchStringFromSelection: EditorBooleanOption.boolean(input.seedSearchStringFromSelection, this.defaultValue.seedSearchStringFromSelection), autoFindInSelection: typeof _input.autoFindInSelection === 'boolean' ? (_input.autoFindInSelection ? 'always' : 'never') diff --git a/src/vs/editor/contrib/find/findModel.ts b/src/vs/editor/contrib/find/findModel.ts index ec25e5727ea..ea1002b23fb 100644 --- a/src/vs/editor/contrib/find/findModel.ts +++ b/src/vs/editor/contrib/find/findModel.ts @@ -70,9 +70,6 @@ export const FIND_IDS = { export const MATCHES_LIMIT = 19999; const RESEARCH_DELAY = 240; -let debounceTimer: ReturnType; -let DEBOUNCE_DURATION = 240; - export class FindModelBoundToEditorModel { private readonly _editor: IActiveCodeEditor; @@ -172,7 +169,7 @@ export class FindModelBoundToEditorModel { return model.getFullModelRange(); } - private research (moveCursor: boolean, newFindScope?: Range | null): void { + private research(moveCursor: boolean, newFindScope?: Range | null): void { let findScope: Range | null = null; if (typeof newFindScope !== 'undefined') { findScope = newFindScope; @@ -208,11 +205,8 @@ export class FindModelBoundToEditorModel { undefined ); - if (moveCursor && this._editor.getOption(EditorOption.find).moveOnType) { - clearTimeout(debounceTimer); - debounceTimer = setTimeout(() => { - this._moveToNextMatch(this._decorations.getStartPosition()); - }, DEBOUNCE_DURATION); + if (moveCursor && this._editor.getOption(EditorOption.find).cursorMoveOnType) { + this._moveToNextMatch(this._decorations.getStartPosition()); } } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 5b37a7be571..0d2b59f1aac 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3289,7 +3289,7 @@ declare namespace monaco.editor { /** * Controls whether the cursor should move to find matches while typing. */ - moveOnType?: boolean; + cursorMoveOnType?: boolean; /** * Controls if we seed search string in the Find Widget with editor selection. */ From 9bd54c15ae0c2017f4ea251e9eb9a88fb38d62c9 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 10 Aug 2020 17:27:44 -0700 Subject: [PATCH 092/736] :lipstick: --- .../contrib/notebook/electron-browser/notebook.contribution.ts | 1 - .../test/browser/api/extHostNotebookConcatDocument.test.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/electron-browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/electron-browser/notebook.contribution.ts index 7609672b040..d6e000d64cd 100644 --- a/src/vs/workbench/contrib/notebook/electron-browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/electron-browser/notebook.contribution.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { isMacintosh } from 'vs/base/common/platform'; import { CopyAction, CutAction, PasteAction } from 'vs/editor/contrib/clipboard/clipboard'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { getActiveNotebookEditor } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; diff --git a/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts b/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts index 3fcaa6da684..f6c718724e0 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts @@ -44,7 +44,7 @@ suite('NotebookConcatDocument', function () { }); extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(rpcProtocol, new NullLogService()); extHostDocuments = new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors); - extHostNotebooks = new ExtHostNotebookController(rpcProtocol, new ExtHostCommands(rpcProtocol, new NullLogService()), extHostDocumentsAndEditors, { isExtensionDevelopmentDebug: false, webviewCspSource: '', webviewResourceRoot: '' }); + extHostNotebooks = new ExtHostNotebookController(rpcProtocol, new ExtHostCommands(rpcProtocol, new NullLogService()), extHostDocumentsAndEditors, { isExtensionDevelopmentDebug: false, webviewCspSource: '', webviewResourceRoot: '' }, new NullLogService()); let reg = extHostNotebooks.registerNotebookContentProvider(nullExtensionDescription, 'test', new class extends mock() { // async openNotebook() { } }); From 6d0ac4702275397def590552cbc475638585ed18 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 10 Aug 2020 20:01:39 -0700 Subject: [PATCH 093/736] Fix RC version --- .../typescript-language-features/src/tsServer/spawner.ts | 8 +++----- extensions/typescript-language-features/src/utils/api.ts | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/extensions/typescript-language-features/src/tsServer/spawner.ts b/extensions/typescript-language-features/src/tsServer/spawner.ts index 092ad4f0fb6..51a4096f9b3 100644 --- a/extensions/typescript-language-features/src/tsServer/spawner.ts +++ b/extensions/typescript-language-features/src/tsServer/spawner.ts @@ -164,11 +164,9 @@ export class TypeScriptServerSpawner { let tsServerLogFile: string | undefined; if (kind === TsServerProcessKind.Syntax) { - if (semver.gte(API.v400rc.fullVersionString, apiVersion.fullVersionString)) { - args.push('--serverMode'); - args.push('partialSemantic'); - } - else { + if (semver.gte(API.v401rc.fullVersionString, apiVersion.fullVersionString)) { + args.push('--serverMode', 'partialSemantic'); + } else { args.push('--syntaxOnly'); } } diff --git a/extensions/typescript-language-features/src/utils/api.ts b/extensions/typescript-language-features/src/utils/api.ts index f797e578121..ea9c2493e8a 100644 --- a/extensions/typescript-language-features/src/utils/api.ts +++ b/extensions/typescript-language-features/src/utils/api.ts @@ -34,7 +34,7 @@ export default class API { public static readonly v380 = API.fromSimpleString('3.8.0'); public static readonly v381 = API.fromSimpleString('3.8.1'); public static readonly v390 = API.fromSimpleString('3.9.0'); - public static readonly v400rc = API.fromSimpleString('4.0.0-rc'); + public static readonly v401rc = API.fromSimpleString('4.0.1-rc'); public static readonly v400 = API.fromSimpleString('4.0.0'); public static fromVersionString(versionString: string): API { From 219120f66769c3c24e78d4b03d477b4164b185f0 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 10 Aug 2020 20:05:59 -0700 Subject: [PATCH 094/736] Use TS RC for building VS Code --- build/package.json | 2 +- build/yarn.lock | 8 ++++---- package.json | 4 ++-- yarn.lock | 8 ++++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build/package.json b/build/package.json index 08f2bf30756..7392e4b602c 100644 --- a/build/package.json +++ b/build/package.json @@ -45,7 +45,7 @@ "minimist": "^1.2.3", "request": "^2.85.0", "terser": "4.3.8", - "typescript": "^4.0.0-dev.20200803", + "typescript": "^4.0.1-rc", "vsce": "1.48.0", "vscode-telemetry-extractor": "^1.6.0", "xml2js": "^0.4.17" diff --git a/build/yarn.lock b/build/yarn.lock index 4aa0234e59f..d610f9cefa7 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -2535,10 +2535,10 @@ typescript@^3.0.1: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== -typescript@^4.0.0-dev.20200803: - version "4.0.0-dev.20200803" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.0-dev.20200803.tgz#ea8b0e9fb2ee3085598ff200c8568f04f4cbb2ba" - integrity sha512-f/jDkFqCs0gbUd5MCUijO9u3AOMx1x1HdRDDHSidlc6uPVEkRduxjeTFhIXbGutO7ivzv+aC2sxH+1FQwsyBcg== +typescript@^4.0.1-rc: + version "4.0.1-rc" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.1-rc.tgz#8adc78223eae56fe71d906a5fa90c3543b07a677" + integrity sha512-TCkspT3dSKOykbzS3/WSK7pqU2h1d/lEO6i45Afm5Y3XNAEAo8YXTG3kHOQk/wFq/5uPyO1+X8rb/Q+g7UsxJw== typical@^4.0.0: version "4.0.0" diff --git a/package.json b/package.json index 46b582a2a37..ddd7346cafb 100644 --- a/package.json +++ b/package.json @@ -164,7 +164,7 @@ "source-map": "^0.4.4", "style-loader": "^1.0.0", "ts-loader": "^4.4.2", - "typescript": "^4.0.0-dev.20200803", + "typescript": "^4.0.1-rc", "typescript-formatter": "7.1.0", "underscore": "^1.8.2", "vinyl": "^2.0.0", @@ -191,4 +191,4 @@ "windows-mutex": "0.3.0", "windows-process-tree": "0.2.4" } -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index b2fbf543af3..65f77846116 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9369,10 +9369,10 @@ typescript@^2.6.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4" integrity sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q= -typescript@^4.0.0-dev.20200803: - version "4.0.0-dev.20200803" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.0-dev.20200803.tgz#ea8b0e9fb2ee3085598ff200c8568f04f4cbb2ba" - integrity sha512-f/jDkFqCs0gbUd5MCUijO9u3AOMx1x1HdRDDHSidlc6uPVEkRduxjeTFhIXbGutO7ivzv+aC2sxH+1FQwsyBcg== +typescript@^4.0.1-rc: + version "4.0.1-rc" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.1-rc.tgz#8adc78223eae56fe71d906a5fa90c3543b07a677" + integrity sha512-TCkspT3dSKOykbzS3/WSK7pqU2h1d/lEO6i45Afm5Y3XNAEAo8YXTG3kHOQk/wFq/5uPyO1+X8rb/Q+g7UsxJw== uc.micro@^1.0.1, uc.micro@^1.0.3: version "1.0.3" From 3bcac20a69784790307a5dc95fd54bde2b8bdc93 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 10 Aug 2020 20:10:17 -0700 Subject: [PATCH 095/736] Fix html folding test for TS bug --- .../html-language-features/server/src/test/folding.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/html-language-features/server/src/test/folding.test.ts b/extensions/html-language-features/server/src/test/folding.test.ts index 272a81463fd..693a74420f0 100644 --- a/extensions/html-language-features/server/src/test/folding.test.ts +++ b/extensions/html-language-features/server/src/test/folding.test.ts @@ -71,7 +71,7 @@ suite('HTML Folding', async () => { /*13*/'', /*14*/'', ]; - await assertRanges(input, [r(0, 13), r(1, 12), r(2, 6), r(3, 6), r(8, 11), r(9, 11)]); + await assertRanges(input, [r(0, 13), r(1, 12), r(2, 6), r(3, 6), r(8, 11), r(9, 11), r(9, 11)]); }); test('Embedded JavaScript - incomplete', async () => { From 7e4fd71ad82f6ff01e4020b1e33a831f129462d3 Mon Sep 17 00:00:00 2001 From: Eric Piacentini <45980161+epiacentini@users.noreply.github.com> Date: Mon, 10 Aug 2020 20:45:11 -0700 Subject: [PATCH 096/736] Removed lines requiring Typescript version < 3.0 (#104211) --- .../package.nls.json | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index 4322984e099..6009e367122 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -9,14 +9,14 @@ "typescript.disableAutomaticTypeAcquisition": "Disables automatic type acquisition. Automatic type acquisition fetches `@types` packages from npm to improve IntelliSense for external libraries.", "typescript.enablePromptUseWorkspaceTsdk": "Enables prompting of users to use the TypeScript version configured in the workspace for Intellisense.", "typescript.tsserver.log": "Enables logging of the TS server to a file. This log can be used to diagnose TS Server issues. The log may contain file paths, source code, and other potentially sensitive information from your project.", - "typescript.tsserver.pluginPaths": "Additional paths to discover TypeScript Language Service plugins. Requires using TypeScript 2.3.0 or newer in the workspace.", + "typescript.tsserver.pluginPaths": "Additional paths to discover TypeScript Language Service plugins.", "typescript.tsserver.pluginPaths.item": "Either an absolute or relative path. Relative path will be resolved against workspace folder(s).", "typescript.tsserver.trace": "Enables tracing of messages sent to the TS server. This trace can be used to diagnose TS Server issues. The trace may contain file paths, source code, and other potentially sensitive information from your project.", "typescript.validate.enable": "Enable/disable TypeScript validation.", "typescript.format.enable": "Enable/disable default TypeScript formatter.", "javascript.format.enable": "Enable/disable default JavaScript formatter.", "format.insertSpaceAfterCommaDelimiter": "Defines space handling after a comma delimiter.", - "format.insertSpaceAfterConstructor": "Defines space handling after the constructor keyword. Requires using TypeScript 2.3.0 or newer in the workspace.", + "format.insertSpaceAfterConstructor": "Defines space handling after the constructor keyword.", "format.insertSpaceAfterSemicolonInForStatements": "Defines space handling after a semicolon in a for statement.", "format.insertSpaceBeforeAndAfterBinaryOperators": "Defines space handling after a binary operator.", "format.insertSpaceAfterKeywordsInControlFlowStatements": "Defines space handling after keywords in a control flow statement.", @@ -24,10 +24,10 @@ "format.insertSpaceBeforeFunctionParenthesis": "Defines space handling before function argument parentheses.", "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": "Defines space handling after opening and before closing non-empty parenthesis.", "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": "Defines space handling after opening and before closing non-empty brackets.", - "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": "Defines space handling after opening and before closing non-empty braces. Requires using TypeScript 2.3.0 or newer in the workspace.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": "Defines space handling after opening and before closing non-empty braces.", "format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": "Defines space handling after opening and before closing template string braces.", "format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": "Defines space handling after opening and before closing JSX expression braces.", - "format.insertSpaceAfterTypeAssertion": "Defines space handling after type assertions in TypeScript. Requires using TypeScript 2.4 or newer in the workspace.", + "format.insertSpaceAfterTypeAssertion": "Defines space handling after type assertions in TypeScript.", "format.placeOpenBraceOnNewLineForFunctions": "Defines whether an open brace is put onto a new line for functions or not.", "format.placeOpenBraceOnNewLineForControlBlocks": "Defines whether an open brace is put onto a new line for control blocks or not.", "format.semicolons": "Defines handling of optional semicolons. Requires using TypeScript 3.7 or newer in the workspace.", @@ -45,8 +45,8 @@ "typescript.restartTsServer": "Restart TS server", "typescript.selectTypeScriptVersion.title": "Select TypeScript Version...", "typescript.reportStyleChecksAsWarnings": "Report style checks as warnings.", - "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.", + "javascript.implicitProjectConfig.checkJs": "Enable/disable semantic checking of JavaScript files. Existing jsconfig.json or tsconfig.json files override this setting.", + "typescript.npm": "Specifies the path to the npm executable used for Automatic Type Acquisition.", "typescript.check.npmIsInstalled": "Check if npm is installed for Automatic Type Acquisition.", "configuration.suggest.names": "Enable/disable including unique names from the file in JavaScript suggestions. Note that name suggestions are always disabled in JavaScript code that is semantically checked using `@ts-check` or `checkJs`.", "typescript.tsc.autoDetect": "Controls auto detection of tsc tasks.", @@ -60,13 +60,13 @@ "configuration.tsserver.useSeparateSyntaxServer": "Enable/disable spawning a separate TypeScript server that can more quickly respond to syntax related operations, such as calculating folding or computing document symbols. Requires using TypeScript 3.4.0 or newer in the workspace.", "configuration.tsserver.maxTsServerMemory": "Set the maximum amount of memory (in MB) to allocate to the TypeScript server process", "configuration.tsserver.experimental.enableProjectDiagnostics": "(Experimental) Enables project wide error reporting.", - "typescript.locale": "Sets the locale used to report JavaScript and TypeScript errors. Requires using TypeScript 2.6.0 or newer in the workspace. Default of `null` uses VS Code's locale.", - "javascript.implicitProjectConfig.experimentalDecorators": "Enable/disable `experimentalDecorators` for JavaScript files that are not part of a project. Existing jsconfig.json or tsconfig.json files override this setting. Requires using TypeScript 2.3.1 or newer in the workspace.", - "configuration.suggest.autoImports": "Enable/disable auto import suggestions. Requires using TypeScript 2.6.1 or newer in the workspace.", + "typescript.locale": "Sets the locale used to report JavaScript and TypeScript errors. Default of `null` uses VS Code's locale.", + "javascript.implicitProjectConfig.experimentalDecorators": "Enable/disable `experimentalDecorators` for JavaScript files that are not part of a project. Existing jsconfig.json or tsconfig.json files override this setting.", + "configuration.suggest.autoImports": "Enable/disable auto import suggestions.", "taskDefinition.tsconfig.description": "The tsconfig file that defines the TS build.", - "javascript.suggestionActions.enabled": "Enable/disable suggestion diagnostics for JavaScript files in the editor. Requires using TypeScript 2.8 or newer in the workspace.", - "typescript.suggestionActions.enabled": "Enable/disable suggestion diagnostics for TypeScript files in the editor. Requires using TypeScript 2.8 or newer in the workspace.", - "typescript.preferences.quoteStyle": "Preferred quote style to use for quick fixes: `single` quotes, `double` quotes, or `auto` infer quote type from existing imports. Requires using TypeScript 2.9 or newer in the workspace.", + "javascript.suggestionActions.enabled": "Enable/disable suggestion diagnostics for JavaScript files in the editor.", + "typescript.suggestionActions.enabled": "Enable/disable suggestion diagnostics for TypeScript files in the editor.", + "typescript.preferences.quoteStyle": "Preferred quote style to use for quick fixes: `single` quotes, `double` quotes, or `auto` infer quote type from existing imports.", "typescript.preferences.importModuleSpecifier": "Preferred path style for auto imports.", "typescript.preferences.importModuleSpecifier.auto": "Automatically select import path style. Prefers using a relative import if `baseUrl` is configured and the relative path has fewer segments than the non-relative import.", "typescript.preferences.importModuleSpecifier.relative": "Relative to the file location.", @@ -80,11 +80,11 @@ "typescript.preferences.includePackageJsonAutoImports.auto": "Search dependencies based on estimated performance impact.", "typescript.preferences.includePackageJsonAutoImports.on": "Always search dependencies.", "typescript.preferences.includePackageJsonAutoImports.off": "Never search dependencies.", - "typescript.updateImportsOnFileMove.enabled": "Enable/disable automatic updating of import paths when you rename or move a file in VS Code. Requires using TypeScript 2.9 or newer in the workspace.", + "typescript.updateImportsOnFileMove.enabled": "Enable/disable automatic updating of import paths when you rename or move a file in VS Code.", "typescript.updateImportsOnFileMove.enabled.prompt": "Prompt on each rename.", "typescript.updateImportsOnFileMove.enabled.always": "Always update paths automatically.", "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.autoClosingTags": "Enable/disable automatic closing of JSX tags.", "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.suggest.completeJSDocs": "Enable/disable suggestion to complete JSDoc comments.", From 2f61d0037ccfb70b8b669898806e2d63dfbddaae Mon Sep 17 00:00:00 2001 From: Andrew Maust Date: Mon, 10 Aug 2020 23:46:14 -0400 Subject: [PATCH 097/736] fixes #104059 (#104193) Co-authored-by: Andrew Maust <69081050+Andrew884@users.noreply.github.com> --- .../src/languageFeatures/completions.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/completions.ts b/extensions/typescript-language-features/src/languageFeatures/completions.ts index b062d8aa854..5c43857fbee 100644 --- a/extensions/typescript-language-features/src/languageFeatures/completions.ts +++ b/extensions/typescript-language-features/src/languageFeatures/completions.ts @@ -641,7 +641,11 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider< const { snippet, parameterCount } = snippetForFunctionCall(item, detail.displayParts); item.insertText = snippet; if (parameterCount > 0) { - commands.push({ title: 'triggerParameterHints', command: 'editor.action.triggerParameterHints' }); + //Fix for https://github.com/microsoft/vscode/issues/104059 + //Don't show parameter hints if "editor.parameterHints.enabled": false + if (vscode.workspace.getConfiguration('editor.parameterHints').get('enabled')) { + commands.push({ title: 'triggerParameterHints', command: 'editor.action.triggerParameterHints' }); + } } } } From 8a57c4cef7f55dd9b542e20b7a561351126cf2ff Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 11 Aug 2020 09:19:36 +0200 Subject: [PATCH 098/736] Revert "custom select box: react on KEY_UP to be aligned with actionBar so appropriate event gets canceled" This reverts commit 1a2a371c487d29b6dec33579c5f106f53a9d7fb7. --- src/vs/base/browser/ui/selectBox/selectBoxCustom.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index c54ff189085..a8f9bb24c5a 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -215,8 +215,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi // Intercept keyboard handling - // React on KEY_UP since the actionBar also reacts on KEY_UP so that appropriate events get canceled - this._register(dom.addDisposableListener(this.selectElement, dom.EventType.KEY_UP, (e: KeyboardEvent) => { + this._register(dom.addDisposableListener(this.selectElement, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { const event = new StandardKeyboardEvent(e); let showDropDown = false; @@ -233,7 +232,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi if (showDropDown) { this.showSelectDropDown(); - dom.EventHelper.stop(e, true); + dom.EventHelper.stop(e); } })); } From a95ae11af2c837097da3ddaf08958785e8e4d489 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 11 Aug 2020 09:43:10 +0200 Subject: [PATCH 099/736] debt - don't duplicate mock util in suggest land --- .../editor/contrib/snippet/test/snippetVariables.test.ts | 2 +- .../contrib/suggest/test/suggestController.test.ts | 2 +- src/vs/editor/contrib/suggest/test/suggestModel.test.ts | 9 +-------- src/vs/editor/contrib/suggest/test/wordDistance.test.ts | 2 +- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts index bb95bdfcadf..a7c03dca8e0 100644 --- a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts @@ -11,7 +11,7 @@ import { SnippetParser, Variable, VariableResolver } from 'vs/editor/contrib/sni import { TextModel } from 'vs/editor/common/model/textModel'; import { Workspace, toWorkspaceFolders, IWorkspace, IWorkspaceContextService, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ILabelService } from 'vs/platform/label/common/label'; -import { mock } from 'vs/editor/contrib/suggest/test/suggestModel.test'; +import { mock } from 'vs/base/test/common/mock'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; suite('Snippet Variables Resolver', function () { diff --git a/src/vs/editor/contrib/suggest/test/suggestController.test.ts b/src/vs/editor/contrib/suggest/test/suggestController.test.ts index ffeccaf1ba3..8644d4498bb 100644 --- a/src/vs/editor/contrib/suggest/test/suggestController.test.ts +++ b/src/vs/editor/contrib/suggest/test/suggestController.test.ts @@ -17,7 +17,7 @@ import { ISuggestMemoryService } from 'vs/editor/contrib/suggest/suggestMemory'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; -import { mock } from 'vs/editor/contrib/suggest/test/suggestModel.test'; +import { mock } from 'vs/base/test/common/mock'; import { Selection } from 'vs/editor/common/core/selection'; import { CompletionProviderRegistry, CompletionItemKind, CompletionItemInsertTextRule } from 'vs/editor/common/modes'; import { Event } from 'vs/base/common/event'; diff --git a/src/vs/editor/contrib/suggest/test/suggestModel.test.ts b/src/vs/editor/contrib/suggest/test/suggestModel.test.ts index edf5997475f..8c75309d28d 100644 --- a/src/vs/editor/contrib/suggest/test/suggestModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/suggestModel.test.ts @@ -34,14 +34,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { MockKeybindingService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; - -export interface Ctor { - new(): T; -} - -export function mock(): Ctor { - return function () { } as any; -} +import { mock } from 'vs/base/test/common/mock'; function createMockEditor(model: TextModel): ITestCodeEditor { diff --git a/src/vs/editor/contrib/suggest/test/wordDistance.test.ts b/src/vs/editor/contrib/suggest/test/wordDistance.test.ts index ede0459b22c..7f1acc79616 100644 --- a/src/vs/editor/contrib/suggest/test/wordDistance.test.ts +++ b/src/vs/editor/contrib/suggest/test/wordDistance.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { EditorSimpleWorker } from 'vs/editor/common/services/editorSimpleWorker'; -import { mock } from 'vs/editor/contrib/suggest/test/suggestModel.test'; +import { mock } from 'vs/base/test/common/mock'; import { EditorWorkerHost, EditorWorkerServiceImpl } from 'vs/editor/common/services/editorWorkerServiceImpl'; import { IModelService } from 'vs/editor/common/services/modelService'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; From f0aedc6541ff252e1e61333f8a44efd91f0c789c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 11 Aug 2020 10:23:18 +0200 Subject: [PATCH 100/736] add skipped test for https://github.com/microsoft/vscode/issues/99504 --- src/vs/editor/contrib/suggest/suggestModel.ts | 2 +- .../contrib/suggest/test/suggestModel.test.ts | 57 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/suggest/suggestModel.ts b/src/vs/editor/contrib/suggest/suggestModel.ts index d3daa598ebb..4a37a446830 100644 --- a/src/vs/editor/contrib/suggest/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/suggestModel.ts @@ -229,7 +229,7 @@ export class SuggestModel implements IDisposable { if (supports) { // keep existing items that where not computed by the // supports/providers that want to trigger now - const items: CompletionItem[] | undefined = this._completionModel ? this._completionModel.adopt(supports) : undefined; + const items = this._completionModel?.adopt(supports); this.trigger({ auto: true, shy: false, triggerCharacter: lastChar }, Boolean(this._completionModel), supports, items); } }; diff --git a/src/vs/editor/contrib/suggest/test/suggestModel.test.ts b/src/vs/editor/contrib/suggest/test/suggestModel.test.ts index 8c75309d28d..a17c34495bc 100644 --- a/src/vs/editor/contrib/suggest/test/suggestModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/suggestModel.test.ts @@ -791,4 +791,61 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { }); }); + + + test('Trigger (full) completions when (incomplete) completions are already active #99504', function () { + this.skip(); + + disposables.push(CompletionProviderRegistry.register({ scheme: 'test' }, { + provideCompletionItems(doc, pos) { + return { + incomplete: true, + suggestions: [{ + kind: CompletionItemKind.Class, + label: 'Z aaa', + insertText: 'Z aaa', + range: new Range(1, 1, pos.lineNumber, pos.column) + }], + }; + } + })); + disposables.push(CompletionProviderRegistry.register({ scheme: 'test' }, { + provideCompletionItems(doc, pos) { + return { + incomplete: false, + suggestions: [{ + kind: CompletionItemKind.Folder, + label: 'aaa', + insertText: 'aaa', + range: getDefaultSuggestRange(doc, pos) + }], + }; + }, + })); + + return withOracle(async (model, editor) => { + + await assertEvent(model.onDidSuggest, () => { + editor.setValue(''); + editor.setSelection(new Selection(1, 1, 1, 1)); + editor.trigger('keyboard', Handler.Type, { text: 'Z' }); + + }, event => { + assert.equal(event.auto, true); + assert.equal(event.completionModel.items.length, 1); + assert.equal(event.completionModel.items[0].textLabel, 'Z aaa'); + }); + + await assertEvent(model.onDidSuggest, () => { + // started another word: Z a| + // item should be: Z aaa, aaa + editor.trigger('keyboard', Handler.Type, { text: ' a' }); + }, event => { + assert.equal(event.auto, true); + assert.equal(event.completionModel.items.length, 2); + assert.equal(event.completionModel.items[0].textLabel, 'Z aaa'); + assert.equal(event.completionModel.items[1].textLabel, 'aaa'); + }); + }); + }); }); From 4c796945e66a6376000e4c77eea0ac28f496f6a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Tue, 11 Aug 2020 11:09:46 +0200 Subject: [PATCH 101/736] resolve typings --- src/vs/workbench/contrib/scm/browser/scmViewPane.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 0106060492d..ce107154888 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -1571,7 +1571,7 @@ export class SCMViewPane extends ViewPane { this._register(actionRunner); this._register(actionRunner.onDidBeforeRun(() => this.tree.domFocus())); - const renderers = [ + const renderers: ICompressibleTreeRenderer[] = [ this.instantiationService.createInstance(RepositoryRenderer, actionViewItemProvider), this.inputRenderer, this.instantiationService.createInstance(ResourceGroupRenderer, actionViewItemProvider), From d9fa2f1d5a5a368e31b16ab4a53ad36787a33ffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Tue, 11 Aug 2020 11:41:14 +0200 Subject: [PATCH 102/736] fix npe --- src/vs/workbench/contrib/scm/browser/scmViewPane.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index ce107154888..faf92760c71 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -40,7 +40,7 @@ import { compareFileNames, comparePaths } from 'vs/base/common/comparers'; import { FuzzyScore, createMatches, IMatch } from 'vs/base/common/filters'; import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; import { localize } from 'vs/nls'; -import { flatten } from 'vs/base/common/arrays'; +import { coalesce, flatten } from 'vs/base/common/arrays'; import { memoize } from 'vs/base/common/decorators'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { toResource, SideBySideEditor } from 'vs/workbench/common/editor'; @@ -889,7 +889,7 @@ class ViewModel { } else if (item) { this.tree.setChildren(item.element, this.render(item).children); } else { - const items = this.scmViewService.visibleRepositories.map(r => this.items.get(r)!); + const items = coalesce(this.scmViewService.visibleRepositories.map(r => this.items.get(r))); this.tree.setChildren(null, items.map(item => this.render(item))); } From 78b0d0a446681dd4a98ced495be2ea81bc9b8708 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Tue, 11 Aug 2020 12:04:42 +0200 Subject: [PATCH 103/736] fake browser event for select all --- src/vs/workbench/browser/actions/listCommands.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/actions/listCommands.ts b/src/vs/workbench/browser/actions/listCommands.ts index c190bf33cc7..5c8d8c70d1e 100644 --- a/src/vs/workbench/browser/actions/listCommands.ts +++ b/src/vs/workbench/browser/actions/listCommands.ts @@ -560,7 +560,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ // List if (focused instanceof List || focused instanceof PagedList) { const list = focused; - list.setSelection(range(list.length)); + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + list.setSelection(range(list.length), fakeKeyboardEvent); } // Trees From 37ebb445e250275cf794de93a37533f7c65f7616 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 11 Aug 2020 12:08:32 +0200 Subject: [PATCH 104/736] re-trigger completions when typing into a new word, https://github.com/microsoft/vscode/issues/99504 --- src/vs/editor/contrib/suggest/suggestModel.ts | 6 ++++++ .../editor/contrib/suggest/test/suggestModel.test.ts | 11 +++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/suggest/suggestModel.ts b/src/vs/editor/contrib/suggest/suggestModel.ts index 4a37a446830..bb4f269ece8 100644 --- a/src/vs/editor/contrib/suggest/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/suggestModel.ts @@ -556,6 +556,12 @@ export class SuggestModel implements IDisposable { return; } + if (ctx.leadingWord.word.length !== 0 && ctx.leadingWord.startColumn > this._context.leadingWord.startColumn) { + // started a new word while IntelliSense shows -> retrigger + this.trigger({ auto: this._context.auto, shy: false }, true); + return; + } + if (ctx.column > this._context.column && this._completionModel.incomplete.size > 0 && ctx.leadingWord.word.length !== 0) { // typed -> moved cursor RIGHT & incomple model & still on a word -> retrigger const { incomplete } = this._completionModel; diff --git a/src/vs/editor/contrib/suggest/test/suggestModel.test.ts b/src/vs/editor/contrib/suggest/test/suggestModel.test.ts index a17c34495bc..360ce3a79d1 100644 --- a/src/vs/editor/contrib/suggest/test/suggestModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/suggestModel.test.ts @@ -794,12 +794,15 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { test('Trigger (full) completions when (incomplete) completions are already active #99504', function () { - this.skip(); + + let countA = 0; + let countB = 0; disposables.push(CompletionProviderRegistry.register({ scheme: 'test' }, { provideCompletionItems(doc, pos) { + countA += 1; return { - incomplete: true, + incomplete: false, // doesn't matter if incomplete or not suggestions: [{ kind: CompletionItemKind.Class, label: 'Z aaa', @@ -811,6 +814,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { })); disposables.push(CompletionProviderRegistry.register({ scheme: 'test' }, { provideCompletionItems(doc, pos) { + countB += 1; return { incomplete: false, suggestions: [{ @@ -845,6 +849,9 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { assert.equal(event.completionModel.items.length, 2); assert.equal(event.completionModel.items[0].textLabel, 'Z aaa'); assert.equal(event.completionModel.items[1].textLabel, 'aaa'); + + assert.equal(countA, 2); // should we keep the suggestions from the "active" provider? + assert.equal(countB, 2); }); }); }); From 9f7139a4d71c4861b01915b8dc631ffb65910b5b Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 11 Aug 2020 12:27:49 +0200 Subject: [PATCH 105/736] Saving untitled file with language mode JSONC uses wrong default extension. Fixes #102032 --- .../services/textfile/browser/textFileService.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 9fe141b27b7..bf0805bb0c1 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -462,13 +462,17 @@ export abstract class AbstractTextFileService extends Disposable implements ITex } suggestFilename(mode: string, untitledName: string) { - const extension = this.modeService.getExtensions(mode)[0]; + const languageName = this.modeService.getLanguageName(mode); + if (!languageName) { + return untitledName; + } + const extension = this.modeService.getExtensions(languageName)[0]; if (extension) { if (!untitledName.endsWith(extension)) { return untitledName + extension; } } - const filename = this.modeService.getFilenames(mode)[0]; + const filename = this.modeService.getFilenames(languageName)[0]; return filename || untitledName; } From 98249fba458d09f9101125b0a8e084f3eadb85ec Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 11 Aug 2020 12:37:23 +0200 Subject: [PATCH 106/736] fix compile error --- src/vs/workbench/test/browser/api/extHostNotebook.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts index ccf9dbe7347..398cd6d442a 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts @@ -43,7 +43,7 @@ suite('NotebookCell#Document', function () { }); extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(rpcProtocol, new NullLogService()); extHostDocuments = new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors); - extHostNotebooks = new ExtHostNotebookController(rpcProtocol, new ExtHostCommands(rpcProtocol, new NullLogService()), extHostDocumentsAndEditors, { isExtensionDevelopmentDebug: false, webviewCspSource: '', webviewResourceRoot: '' }); + extHostNotebooks = new ExtHostNotebookController(rpcProtocol, new ExtHostCommands(rpcProtocol, new NullLogService()), extHostDocumentsAndEditors, { isExtensionDevelopmentDebug: false, webviewCspSource: '', webviewResourceRoot: '' }, new NullLogService()); let reg = extHostNotebooks.registerNotebookContentProvider(nullExtensionDescription, 'test', new class extends mock() { // async openNotebook() { } }); From 14dabea7c2abd4e6002b66945d3d5d7832861983 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 11 Aug 2020 12:59:36 +0200 Subject: [PATCH 107/736] playground/mem.fs: avoid windows defender warning --- extensions/vscode-web-playground/src/memfs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/vscode-web-playground/src/memfs.ts b/extensions/vscode-web-playground/src/memfs.ts index 9a135109fb4..969b2c4b6fa 100644 --- a/extensions/vscode-web-playground/src/memfs.ts +++ b/extensions/vscode-web-playground/src/memfs.ts @@ -105,9 +105,9 @@ export class MemFS implements FileSystemProvider, FileSearchProvider, TextSearch this.writeFile(Uri.parse(`memfs:/sample-folder/file.md`), textEncoder.encode(debuggableFile), { create: true, overwrite: true }); this.writeFile(Uri.parse(`memfs:/sample-folder/file.xml`), textEncoder.encode(''), { create: true, overwrite: true }); this.writeFile(Uri.parse(`memfs:/sample-folder/file.py`), textEncoder.encode('import base64, sys; base64.decode(open(sys.argv[1], "rb"), open(sys.argv[2], "wb"))'), { create: true, overwrite: true }); - this.writeFile(Uri.parse(`memfs:/sample-folder/file.php`), textEncoder.encode('&1\'); ?>'), { create: true, overwrite: true }); this.writeFile(Uri.parse(`memfs:/sample-folder/file.yaml`), textEncoder.encode('- just: write something'), { create: true, overwrite: true }); this.writeFile(Uri.parse(`memfs:/sample-folder/file.jpg`), getImageFile(), { create: true, overwrite: true }); + this.writeFile(Uri.parse(`memfs:/sample-folder/file.php`), textEncoder.encode('&1\'); ?>'), { create: true, overwrite: true }); // some more files & folders this.createDirectory(Uri.parse(`memfs:/sample-folder/folder/`)); From 69186de47d2c6f1ea9c4e51bdbc8ac7bf71f9902 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 11 Aug 2020 14:17:10 +0200 Subject: [PATCH 108/736] callStack: change "load more frame" to "load all frames" fixes #102048 --- .../contrib/debug/browser/callStackView.ts | 61 ++++++++++--------- .../debug/browser/media/debugViewlet.css | 2 +- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 96b2a9827d9..2642d372de5 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -222,7 +222,7 @@ export class CallStackView extends ViewPane { new ThreadsRenderer(this.instantiationService), this.instantiationService.createInstance(StackFramesRenderer), new ErrorsRenderer(), - new LoadMoreRenderer(this.themeService), + new LoadAllRenderer(this.themeService), new ShowMoreRenderer(this.themeService) ], this.dataSource, { accessibilityProvider: new CallStackAccessibilityProvider(), @@ -252,7 +252,7 @@ export class CallStackView extends ViewPane { return e; } if (e instanceof ThreadAndSessionIds) { - return LoadMoreRenderer.LABEL; + return LoadAllRenderer.LABEL; } return nls.localize('showMoreStackFrames2', "Show More Stack Frames"); @@ -273,7 +273,7 @@ export class CallStackView extends ViewPane { this.tree.setInput(this.debugService.getModel()); - this._register(this.tree.onDidOpen(e => { + this._register(this.tree.onDidOpen(async e => { if (this.ignoreSelectionChangedEvent) { return; } @@ -302,8 +302,11 @@ export class CallStackView extends ViewPane { const session = this.debugService.getModel().getSession(element.sessionId); const thread = session && session.getThread(element.threadId); if (thread) { - (thread).fetchCallStack() - .then(() => this.tree.updateChildren()); + const totalFrames = thread.stoppedDetails?.totalFrames; + const remainingFramesCount = typeof totalFrames === 'number' ? (totalFrames - thread.getCallStack().length) : undefined; + // Get all the remaining frames + await (thread).fetchCallStack(remainingFramesCount); + await this.tree.updateChildren(); } } if (element instanceof Array) { @@ -704,18 +707,18 @@ class ErrorsRenderer implements ICompressibleTreeRenderer { - static readonly ID = 'loadMore'; - static readonly LABEL = nls.localize('loadMoreStackFrames', "Load More Stack Frames"); +class LoadAllRenderer implements ICompressibleTreeRenderer { + static readonly ID = 'loadAll'; + static readonly LABEL = nls.localize('loadAllStackFrames', "Load All Stack Frames"); constructor(private readonly themeService: IThemeService) { } get templateId(): string { - return LoadMoreRenderer.ID; + return LoadAllRenderer.ID; } renderTemplate(container: HTMLElement): ILabelTemplateData { - const label = dom.append(container, $('.load-more')); + const label = dom.append(container, $('.load-all')); const toDispose = attachStylerCallback(this.themeService, { textLinkForeground }, colors => { if (colors.textLinkForeground) { label.style.color = colors.textLinkForeground.toString(); @@ -726,7 +729,7 @@ class LoadMoreRenderer implements ICompressibleTreeRenderer, index: number, data: ILabelTemplateData): void { - data.label.textContent = LoadMoreRenderer.LABEL; + data.label.textContent = LoadAllRenderer.LABEL; } renderCompressedElements(node: ITreeNode, FuzzyScore>, index: number, templateData: ILabelTemplateData, height: number | undefined): void { @@ -804,7 +807,7 @@ class CallStackDelegate implements IListVirtualDelegate { return ErrorsRenderer.ID; } if (element instanceof ThreadAndSessionIds) { - return LoadMoreRenderer.ID; + return LoadAllRenderer.ID; } // element instanceof Array @@ -899,29 +902,27 @@ class CallStackDataSource implements IAsyncDataSource> { + private async getThreadCallstack(thread: Thread): Promise> { let callStack: any[] = thread.getCallStack(); - let callStackPromise: Promise = Promise.resolve(null); if (!callStack || !callStack.length) { - callStackPromise = thread.fetchCallStack().then(() => callStack = thread.getCallStack()); + await thread.fetchCallStack(); + callStack = thread.getCallStack(); } - return callStackPromise.then(() => { - if (callStack.length === 1 && thread.session.capabilities.supportsDelayedStackTraceLoading && thread.stoppedDetails && thread.stoppedDetails.totalFrames && thread.stoppedDetails.totalFrames > 1) { - // To reduce flashing of the call stack view simply append the stale call stack - // once we have the correct data the tree will refresh and we will no longer display it. - callStack = callStack.concat(thread.getStaleCallStack().slice(1)); - } + if (callStack.length === 1 && thread.session.capabilities.supportsDelayedStackTraceLoading && thread.stoppedDetails && thread.stoppedDetails.totalFrames && thread.stoppedDetails.totalFrames > 1) { + // To reduce flashing of the call stack view simply append the stale call stack + // once we have the correct data the tree will refresh and we will no longer display it. + callStack = callStack.concat(thread.getStaleCallStack().slice(1)); + } - if (thread.stoppedDetails && thread.stoppedDetails.framesErrorMessage) { - callStack = callStack.concat([thread.stoppedDetails.framesErrorMessage]); - } - if (thread.stoppedDetails && thread.stoppedDetails.totalFrames && thread.stoppedDetails.totalFrames > callStack.length && callStack.length > 1) { - callStack = callStack.concat([new ThreadAndSessionIds(thread.session.getId(), thread.threadId)]); - } + if (thread.stoppedDetails && thread.stoppedDetails.framesErrorMessage) { + callStack = callStack.concat([thread.stoppedDetails.framesErrorMessage]); + } + if (thread.stoppedDetails && thread.stoppedDetails.totalFrames && thread.stoppedDetails.totalFrames > callStack.length && callStack.length > 1) { + callStack = callStack.concat([new ThreadAndSessionIds(thread.session.getId(), thread.threadId)]); + } - return callStack; - }); + return callStack; } } @@ -949,7 +950,7 @@ class CallStackAccessibilityProvider implements IListAccessibilityProvider Date: Tue, 11 Aug 2020 14:24:16 +0200 Subject: [PATCH 109/736] ref count document data on extension host side --- .../workbench/api/common/extHostDocuments.ts | 2 +- .../api/common/extHostDocumentsAndEditors.ts | 63 ++++++++++++------- .../workbench/api/common/extHostNotebook.ts | 14 +---- .../test/browser/api/extHostNotebook.test.ts | 4 +- 4 files changed, 45 insertions(+), 38 deletions(-) diff --git a/src/vs/workbench/api/common/extHostDocuments.ts b/src/vs/workbench/api/common/extHostDocuments.ts index ab8d770765b..033b4262226 100644 --- a/src/vs/workbench/api/common/extHostDocuments.ts +++ b/src/vs/workbench/api/common/extHostDocuments.ts @@ -53,7 +53,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape { } public getAllDocumentData(): ExtHostDocumentData[] { - return this._documentsAndEditors.allDocuments(); + return [...this._documentsAndEditors.allDocuments()]; } public getDocumentData(resource: vscode.Uri): ExtHostDocumentData | undefined { diff --git a/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts b/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts index 63fc98becaa..00718254a0f 100644 --- a/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts +++ b/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts @@ -16,6 +16,18 @@ import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { ILogService } from 'vs/platform/log/common/log'; import { ResourceMap } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; +import { Iterable } from 'vs/base/common/iterator'; + +class Reference { + private _count = 0; + constructor(readonly value: T) { } + ref() { + this._count++; + } + unref() { + return --this._count === 0; + } +} export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsShape { @@ -24,7 +36,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha private _activeEditorId: string | null = null; private readonly _editors = new Map(); - private readonly _documents = new ResourceMap(); + private readonly _documents = new ResourceMap>(); private readonly _onDidAddDocuments = new Emitter(); private readonly _onDidRemoveDocuments = new Emitter(); @@ -51,9 +63,9 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha for (const uriComponent of delta.removedDocuments) { const uri = URI.revive(uriComponent); const data = this._documents.get(uri); - this._documents.delete(uri); - if (data) { - removedDocuments.push(data); + if (data?.unref()) { + this._documents.delete(uri); + removedDocuments.push(data.value); } } } @@ -61,26 +73,31 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha if (delta.addedDocuments) { for (const data of delta.addedDocuments) { const resource = URI.revive(data.uri); - const existingDocumentData = this._documents.get(resource); - if (existingDocumentData) { + let ref = this._documents.get(resource); + + // double check -> only notebook cell documents should be + // referenced/opened more than once... + if (ref) { if (resource.scheme !== Schemas.vscodeNotebookCell) { throw new Error(`document '${resource} already exists!'`); } - existingDocumentData.onEvents({ changes: [], versionId: data.versionId, eol: data.EOL }); - continue; + } + if (!ref) { + ref = new Reference(new ExtHostDocumentData( + this._extHostRpc.getProxy(MainContext.MainThreadDocuments), + resource, + data.lines, + data.EOL, + data.modeId, + data.versionId, + data.isDirty + )); } - const documentData = new ExtHostDocumentData( - this._extHostRpc.getProxy(MainContext.MainThreadDocuments), - resource, - data.lines, - data.EOL, - data.modeId, - data.versionId, - data.isDirty - ); - this._documents.set(resource, documentData); - addedDocuments.push(documentData); + ref.ref(); + + this._documents.set(resource, ref); + addedDocuments.push(ref.value); } } @@ -100,7 +117,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha assert.ok(this._documents.has(resource), `document '${resource}' does not exist`); assert.ok(!this._editors.has(data.id), `editor '${data.id}' already exists!`); - const documentData = this._documents.get(resource)!; + const documentData = this._documents.get(resource)!.value; const editor = new ExtHostTextEditor( data.id, this._extHostRpc.getProxy(MainContext.MainThreadTextEditors), @@ -140,11 +157,11 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha } getDocument(uri: URI): ExtHostDocumentData | undefined { - return this._documents.get(uri); + return this._documents.get(uri)?.value; } - allDocuments(): ExtHostDocumentData[] { - return [...this._documents.values()]; + allDocuments(): Iterable { + return Iterable.map(this._documents.values(), ref => ref.value); } getEditor(id: string): ExtHostTextEditor | undefined { diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 9628a0b9912..eeecd1409b8 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -67,7 +67,7 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell { modeId: cell.language, uri: cell.uri, isDirty: false, - versionId: -1 + versionId: 1 }; } @@ -1523,19 +1523,9 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN const document = this._documents.get(revivedUriStr); if (document) { - - // remove all documents that have not been opened by the renderer - // and have not yet been closed - const removedCellDocuments: URI[] = []; - for (let cell of document.cells) { - if (this._documentsAndEditors.getDocument(cell.uri)?.version === -1) { - removedCellDocuments.push(cell.uri); - } - } - document.dispose(); this._documents.delete(revivedUriStr); - this._documentsAndEditors.$acceptDocumentsAndEditorsDelta({ removedDocuments: removedCellDocuments }); + this._documentsAndEditors.$acceptDocumentsAndEditorsDelta({ removedDocuments: document.cells.map(cell => cell.uri) }); this._onDidCloseNotebookDocument.fire(document); } diff --git a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts index 398cd6d442a..b1aaf836009 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts @@ -96,12 +96,12 @@ suite('NotebookCell#Document', function () { assert.ok(d1); assert.equal(d1.languageId, c1.language); - assert.equal(d1.version, -1); // we use -1 as signal that the document was created on the ext-host side + assert.equal(d1.version, 1); const d2 = extHostDocuments.getDocument(c2.uri); assert.ok(d2); assert.equal(d2.languageId, c2.language); - assert.equal(d2.version, -1); + assert.equal(d2.version, 1); }); test('cell document goes when notebook closes', async function () { From 216c847662cc31221202c4913a3193de7487ec72 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 11 Aug 2020 14:49:42 +0200 Subject: [PATCH 110/736] Watch terminal for ports to forward when remote Fixes microsoft/vscode-remote-release#3235 --- src/vs/platform/remote/common/tunnel.ts | 2 - .../contrib/remote/browser/remote.ts | 4 +- .../contrib/remote/browser/tunnelView.ts | 10 ++- .../contrib/remote/browser/urlFinder.ts | 71 +++++++++++++++++++ 4 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 src/vs/workbench/contrib/remote/browser/urlFinder.ts diff --git a/src/vs/platform/remote/common/tunnel.ts b/src/vs/platform/remote/common/tunnel.ts index c2275c2948c..fc98af96bcd 100644 --- a/src/vs/platform/remote/common/tunnel.ts +++ b/src/vs/platform/remote/common/tunnel.ts @@ -56,8 +56,6 @@ export function extractLocalHostUriMetaDataForPortMapping(uri: URI): { address: }; } - - export abstract class AbstractTunnelService implements ITunnelService { declare readonly _serviceBrand: undefined; diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index 6f0a32f639a..e9a398cc506 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -56,6 +56,7 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { RemoteWindowActiveIndicator } from 'vs/workbench/contrib/remote/browser/remoteIndicator'; import { inQuickPickContextKeyValue } from 'vs/workbench/browser/quickaccess'; import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; export interface HelpInformation { extensionDescription: IExtensionDescription; @@ -481,6 +482,7 @@ export class RemoteViewPaneContainer extends FilterViewPaneContainer implements @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @ITerminalService private readonly terminalService: ITerminalService ) { super(VIEWLET_ID, remoteExplorerService.onDidChangeTargetType, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, viewDescriptorService); this.addConstantViewDescriptors([this.helpPanelDescriptor]); @@ -555,7 +557,7 @@ export class RemoteViewPaneContainer extends FilterViewPaneContainer implements // This context key is set to false in the constructor, but is expected to be changed by resolver extensions to enable the forwarded ports view. const viewEnabled: boolean = !!forwardedPortsViewEnabled.getValue(this.contextKeyService); if (this.environmentService.configuration.remoteAuthority && !this.tunnelPanelDescriptor && viewEnabled) { - this.tunnelPanelDescriptor = new TunnelPanelDescriptor(new TunnelViewModel(this.remoteExplorerService), this.environmentService); + this.tunnelPanelDescriptor = new TunnelPanelDescriptor(new TunnelViewModel(this.remoteExplorerService, this.terminalService), this.environmentService); const viewsRegistry = Registry.as(Extensions.ViewsRegistry); viewsRegistry.registerViews([this.tunnelPanelDescriptor!], this.viewContainer); } diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index 930d4e799cc..32683bf8418 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -42,6 +42,8 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { UrlFinder } from 'vs/workbench/contrib/remote/browser/urlFinder'; export const forwardedPortsViewEnabled = new RawContextKey('forwardedPortsViewEnabled', false); @@ -72,7 +74,8 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel { private _candidates: Map = new Map(); constructor( - @IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService) { + @IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService, + @ITerminalService readonly terminalService: ITerminalService) { super(); this.model = remoteExplorerService.tunnelModel; this._register(this.model.onForwardPort(() => this._onForwardedPortsChanged.fire())); @@ -86,6 +89,11 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel { remotePort: 0, description: '' }; + + const urlFinder = this._register(new UrlFinder(terminalService)); + this._register(urlFinder.onDidMatchLocalUrl(localUrl => { + this.model.forward(localUrl); + })); } async groups(): Promise { diff --git a/src/vs/workbench/contrib/remote/browser/urlFinder.ts b/src/vs/workbench/contrib/remote/browser/urlFinder.ts new file mode 100644 index 00000000000..e72dc1fdeb0 --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/urlFinder.ts @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ITerminalInstance, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; + +export class UrlFinder extends Disposable { + private static readonly terminalCodesRegex = /(?:\u001B|\u009B)[\[\]()#;?]*(?:(?:(?:[a-zA-Z0-9]*(?:;[a-zA-Z0-9]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[0-9A-PR-TZcf-ntqry=><~]))/g; + /** + * Local server url pattern matching following urls: + * http://localhost:3000/ - commonly used across multiple frameworks + * https://127.0.0.1:5001/ - ASP.NET + * http://:8080 - Beego Golang + * http://0.0.0.0:4000 - Elixir Phoenix + */ + private static readonly localUrlRegex = /\b\w{2,20}:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0|:\d{2,5})[\w\-\.\~:\/\?\#[\]\@!\$&\(\)\*\+\,\;\=]*/gim; + + private _onDidMatchLocalUrl: Emitter<{ host: string, port: number }> = new Emitter(); + public readonly onDidMatchLocalUrl = this._onDidMatchLocalUrl.event; + private listeners: Map = new Map(); + + constructor(terminalService: ITerminalService) { + super(); + terminalService.terminalInstances.forEach(instance => { + this.listeners.set(instance, instance.onData(data => { + this.processData(data); + })); + }); + this._register(terminalService.onInstanceCreated(instance => { + this.listeners.set(instance, instance.onData(data => { + this.processData(data); + })); + })); + this._register(terminalService.onInstanceDisposed(instance => { + this.listeners.delete(instance); + })); + } + + dispose() { + super.dispose(); + const listeners = this.listeners.values(); + for (const listener of listeners) { + listener.dispose(); + } + } + + private processData(data: string) { + // strip ANSI terminal codes + data = data.replace(UrlFinder.terminalCodesRegex, ''); + const urlMatches = data.match(UrlFinder.localUrlRegex) || []; + urlMatches.forEach((match) => { + // check if valid url + const serverUrl = new URL(match); + if (serverUrl) { + // check if the port is a valid integer value + const port = parseFloat(serverUrl.port!); + if (!isNaN(port) && Number.isInteger(port) && port > 0 && port <= 65535) { + // normalize the host name + let host = serverUrl.hostname; + if (host !== '0.0.0.0') { + host = 'localhost'; + } + this._onDidMatchLocalUrl.fire({ port, host }); + } + } + }); + } +} From 0bf0ecf9de78d1c4b87342395be4b0bb2482c556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Tue, 11 Aug 2020 15:53:55 +0200 Subject: [PATCH 111/736] scm repositories view pane --- .../contrib/scm/browser/media/scm.css | 11 + .../contrib/scm/browser/scm.contribution.ts | 24 ++- .../scm/browser/scmRepositoriesViewPane.ts | 195 ++++++++++++++++++ .../scm/browser/scmRepositoryRenderer.ts | 15 +- .../contrib/scm/browser/scmViewPane.ts | 13 +- .../contrib/scm/browser/scmViewService.ts | 23 ++- src/vs/workbench/contrib/scm/common/scm.ts | 6 + .../contrib/scm/common/scmService.ts | 12 +- 8 files changed, 278 insertions(+), 21 deletions(-) create mode 100644 src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts diff --git a/src/vs/workbench/contrib/scm/browser/media/scm.css b/src/vs/workbench/contrib/scm/browser/media/scm.css index 9947f240bf2..5fe8b5d58e8 100644 --- a/src/vs/workbench/contrib/scm/browser/media/scm.css +++ b/src/vs/workbench/contrib/scm/browser/media/scm.css @@ -232,3 +232,14 @@ .scm-view .scm-editor-container .mtk1 { color: inherit; } + +/* Repositories */ + +.scm-repositories-view .scm-provider { + margin: 0 12px 0 20px; + overflow: hidden; +} + +.scm-repositories-view .scm-provider > .label > .name { + font-weight: normal; +} diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index 9b2876239e8..f5a22758da5 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { DirtyDiffWorkbenchController } from './dirtydiffDecorator'; -import { VIEWLET_ID, ISCMRepository, ISCMService, VIEW_PANE_ID, ISCMProvider, ISCMViewService } from 'vs/workbench/contrib/scm/common/scm'; +import { VIEWLET_ID, ISCMRepository, ISCMService, VIEW_PANE_ID, ISCMProvider, ISCMViewService, REPOSITORIES_VIEW_PANE_ID } from 'vs/workbench/contrib/scm/common/scm'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { SCMStatusController } from './activity'; @@ -25,6 +25,7 @@ import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; import { Codicon } from 'vs/base/common/codicons'; import { SCMViewPane } from 'vs/workbench/contrib/scm/browser/scmViewPane'; import { SCMViewService } from 'vs/workbench/contrib/scm/browser/scmViewService'; +import { SCMRepositoriesViewPane } from 'vs/workbench/contrib/scm/browser/scmRepositoriesViewPane'; ModesRegistry.registerLanguage({ id: 'scminput', @@ -60,6 +61,22 @@ viewsRegistry.registerViews([{ canToggleVisibility: true, workspace: true, canMoveView: true, + weight: 80, + containerIcon: Codicon.sourceControl.classNames +}], viewContainer); + +viewsRegistry.registerViews([{ + id: REPOSITORIES_VIEW_PANE_ID, + name: localize('source control repositories', "Source Control Repositories"), + ctorDescriptor: new SyncDescriptor(SCMRepositoriesViewPane), + canToggleVisibility: true, + hideByDefault: true, + workspace: true, + canMoveView: true, + weight: 20, + order: -1000, + when: ContextKeyExpr.and(ContextKeyExpr.has('scm.providerCount'), ContextKeyExpr.notEquals('scm.providerCount', 0)), + // readonly when = ContextKeyExpr.or(ContextKeyExpr.equals('config.scm.alwaysShowProviders', true), ContextKeyExpr.and(ContextKeyExpr.notEquals('scm.providerCount', 0), ContextKeyExpr.notEquals('scm.providerCount', 1))); containerIcon: Codicon.sourceControl.classNames }], viewContainer); @@ -169,6 +186,11 @@ Registry.as(ConfigurationExtensions.Configuration).regis type: 'boolean', markdownDescription: localize('alwaysShowRepository', "Controls whether repositories should always be visible in the SCM view."), default: false + }, + 'scm.repositories.visible': { + type: 'number', + description: localize('providersVisible', "Controls how many repositories are visible in the Source Control Repositories section. Set to `0` to be able to manually resize the view."), + default: 10 } } }); diff --git a/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts new file mode 100644 index 00000000000..638d743df38 --- /dev/null +++ b/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts @@ -0,0 +1,195 @@ +/*--------------------------------------------------------------------------------------------- + * 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/scm'; +import { localize } from 'vs/nls'; +import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { append, $ } from 'vs/base/browser/dom'; +import { IListVirtualDelegate, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; +import { ISCMRepository, ISCMViewService } from 'vs/workbench/contrib/scm/common/scm'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IAction, IActionViewItem } from 'vs/base/common/actions'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { WorkbenchList } from 'vs/platform/list/browser/listService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IViewDescriptorService } from 'vs/workbench/common/views'; +import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { RepositoryRenderer } from 'vs/workbench/contrib/scm/browser/scmRepositoryRenderer'; +import { collectContextMenuActions, StatusBarAction, StatusBarActionViewItem } from 'vs/workbench/contrib/scm/browser/util'; + +class ListDelegate implements IListVirtualDelegate { + + getHeight(): number { + return 22; + } + + getTemplateId(): string { + return RepositoryRenderer.TEMPLATE_ID; + } +} + +export class SCMRepositoriesViewPane extends ViewPane { + + private list!: WorkbenchList; + + constructor( + options: IViewPaneOptions, + @ISCMViewService protected scmViewService: ISCMViewService, + @IKeybindingService protected keybindingService: IKeybindingService, + @IContextMenuService protected contextMenuService: IContextMenuService, + @IInstantiationService instantiationService: IInstantiationService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @IContextKeyService contextKeyService: IContextKeyService, + @IConfigurationService configurationService: IConfigurationService, + @IOpenerService openerService: IOpenerService, + @IThemeService themeService: IThemeService, + @ITelemetryService telemetryService: ITelemetryService + ) { + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); + } + + protected renderBody(container: HTMLElement): void { + super.renderBody(container); + + const listContainer = append(container, $('.scm-view.scm-repositories-view')); + + const delegate = new ListDelegate(); + const renderer = this.instantiationService.createInstance(RepositoryRenderer, a => this.getActionViewItem(a),); + const identityProvider = { getId: (r: ISCMRepository) => r.provider.id }; + + this.list = this.instantiationService.createInstance(WorkbenchList, `SCM Main`, listContainer, delegate, [renderer], { + identityProvider, + horizontalScrolling: false, + overrideStyles: { + listBackground: SIDE_BAR_BACKGROUND + }, + accessibilityProvider: { + getAriaLabel(r: ISCMRepository) { + return r.provider.label; + }, + getWidgetAriaLabel() { + return localize('scm', "Source Control Repositories"); + } + } + }) as WorkbenchList; + + this._register(this.list); + this._register(this.list.onDidChangeSelection(this.onListSelectionChange, this)); + this._register(this.list.onContextMenu(this.onListContextMenu, this)); + + this._register(this.scmViewService.onDidChangeVisibleRepositories(this.updateListSelection, this)); + + this._register(this.scmViewService.onDidAddRepository(this.onDidAddRepository, this)); + this._register(this.scmViewService.onDidRemoveRepository(this.onDidRemoveRepository, this)); + + for (const repository of this.scmViewService.repositories) { + this.onDidAddRepository(repository); + } + + this._register(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('scm.repositories.visible')) { + this.updateBodySize(); + } + })); + + this.updateListSelection(); + } + + private onDidAddRepository(repository: ISCMRepository): void { + this.list.splice(this.list.length, 0, [repository]); + this.updateBodySize(); + } + + private onDidRemoveRepository(repository: ISCMRepository): void { + const index = this.list.indexOf(repository); + + if (index > -1) { + this.list.splice(index, 1); + } + + this.updateBodySize(); + } + + focus(): void { + this.list.domFocus(); + } + + protected layoutBody(height: number, width: number): void { + super.layoutBody(height, width); + this.list.layout(height, width); + } + + private updateBodySize(): void { + const visibleCount = this.configurationService.getValue('scm.repositories.visible'); + const empty = this.list.length === 0; + const size = Math.min(this.list.length, visibleCount) * 22; + + this.minimumBodySize = visibleCount === 0 ? 22 : size; + this.maximumBodySize = visibleCount === 0 ? Number.POSITIVE_INFINITY : empty ? Number.POSITIVE_INFINITY : size; + } + + private onListContextMenu(e: IListContextMenuEvent): void { + if (!e.element) { + return; + } + + const provider = e.element.provider; + const menus = this.scmViewService.menus.getRepositoryMenus(provider); + const menu = menus.repositoryMenu; + const [actions, disposable] = collectContextMenuActions(menu, this.contextMenuService); + + this.contextMenuService.showContextMenu({ + getAnchor: () => e.anchor, + getActions: () => actions, + getActionsContext: () => provider, + onHide() { + disposable.dispose(); + } + }); + } + + private onListSelectionChange(e: IListEvent): void { + if (e.browserEvent && e.elements.length > 0) { + const scrollTop = this.list.scrollTop; + this.scmViewService.visibleRepositories = e.elements; + this.list.scrollTop = scrollTop; + } + } + + private updateListSelection(): void { + const set = new Set(); + + for (const repository of this.scmViewService.visibleRepositories) { + set.add(repository); + } + + const selection: number[] = []; + + for (let i = 0; i < this.list.length; i++) { + if (set.has(this.list.element(i))) { + selection.push(i); + } + } + + this.list.setSelection(selection); + + if (selection.length > 0) { + this.list.setFocus([selection[0]]); + } + } + + getActionViewItem(action: IAction): IActionViewItem | undefined { + if (action instanceof StatusBarAction) { + return new StatusBarActionViewItem(action); + } + + return super.getActionViewItem(action); + } +} diff --git a/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts b/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts index 1ab1da3ab69..b544987ea9d 100644 --- a/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts +++ b/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts @@ -13,12 +13,13 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { ICommandService } from 'vs/platform/commands/common/commands'; import { IAction, IActionViewItemProvider } from 'vs/base/common/actions'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { connectPrimaryMenu, StatusBarAction } from './util'; +import { connectPrimaryMenu, isSCMRepository, StatusBarAction } from './util'; import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; import { ITreeNode } from 'vs/base/browser/ui/tree/tree'; import { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree'; import { FuzzyScore } from 'vs/base/common/filters'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; +import { IListRenderer } from 'vs/base/browser/ui/list/list'; interface RepositoryTemplate { readonly label: HTMLElement; @@ -31,7 +32,7 @@ interface RepositoryTemplate { readonly templateDisposable: IDisposable; } -export class RepositoryRenderer implements ICompressibleTreeRenderer { +export class RepositoryRenderer implements ICompressibleTreeRenderer, IListRenderer { static readonly TEMPLATE_ID = 'repository'; get templateId(): string { return RepositoryRenderer.TEMPLATE_ID; } @@ -46,7 +47,9 @@ export class RepositoryRenderer implements ICompressibleTreeRenderer, index: number, templateData: RepositoryTemplate, height: number | undefined): void { + renderElement(arg: ISCMRepository | ITreeNode, index: number, templateData: RepositoryTemplate, height: number | undefined): void { templateData.disposable.dispose(); const disposables = new DisposableStore(); - const repository = node.element; + const repository = isSCMRepository(arg) ? arg : arg.element; if (repository.provider.rootUri) { templateData.label.title = `${repository.provider.label}: ${repository.provider.rootUri.fsPath}`; @@ -115,7 +118,7 @@ export class RepositoryRenderer implements ICompressibleTreeRenderer, index: number, template: RepositoryTemplate): void { + disposeElement(group: ISCMRepository | ITreeNode, index: number, template: RepositoryTemplate): void { template.disposable.dispose(); } diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index faf92760c71..e0232e22d8d 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -10,7 +10,7 @@ import { IDisposable, Disposable, DisposableStore, combinedDisposable, dispose } import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { append, $, addClass, toggleClass, removeClass, Dimension } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { ISCMResourceGroup, ISCMResource, InputValidationType, ISCMService, ISCMRepository, ISCMInput, IInputValidation, ISCMViewService, ISCMViewVisibleRepositoryChangeEvent } from 'vs/workbench/contrib/scm/common/scm'; +import { ISCMResourceGroup, ISCMResource, InputValidationType, ISCMRepository, ISCMInput, IInputValidation, ISCMViewService, ISCMViewVisibleRepositoryChangeEvent } from 'vs/workbench/contrib/scm/common/scm'; import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -1516,7 +1516,6 @@ export class SCMViewPane extends ViewPane { constructor( options: IViewPaneOptions, - @ISCMService private scmService: ISCMService, @ISCMViewService private scmViewService: ISCMViewService, @IKeybindingService protected keybindingService: IKeybindingService, @IThemeService protected themeService: IThemeService, @@ -1534,7 +1533,7 @@ export class SCMViewPane extends ViewPane { @ITelemetryService telemetryService: ITelemetryService, ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); - this._register(Event.any(this.scmService.onDidAddRepository, this.scmService.onDidRemoveRepository)(() => this._onDidChangeViewWelcomeState.fire())); + this._register(Event.any(this.scmViewService.onDidAddRepository, this.scmViewService.onDidRemoveRepository)(() => this._onDidChangeViewWelcomeState.fire())); this._register(this.scmViewService.menus.titleMenu.onDidChangeTitle(this.updateActions, this)); } @@ -1726,12 +1725,12 @@ export class SCMViewPane extends ViewPane { return; } else if (isSCMResourceGroup(e.element)) { // TODO@joao: remove const provider = e.element.provider; - const repository = this.scmService.repositories.find(r => r.provider === provider); + const repository = this.scmViewService.repositories.find(r => r.provider === provider); repository?.setSelected(true); return; } else if (ResourceTree.isResourceNode(e.element)) { // TODO@joao: remove const provider = e.element.context.provider; - const repository = this.scmService.repositories.find(r => r.provider === provider); + const repository = this.scmViewService.repositories.find(r => r.provider === provider); repository?.setSelected(true); return; } else if (isSCMInput(e.element)) { @@ -1765,7 +1764,7 @@ export class SCMViewPane extends ViewPane { // TODO@joao: remove const provider = e.element.resourceGroup.provider; - const repository = this.scmService.repositories.find(r => r.provider === provider); + const repository = this.scmViewService.repositories.find(r => r.provider === provider); repository?.setSelected(true); } @@ -1826,7 +1825,7 @@ export class SCMViewPane extends ViewPane { } shouldShowWelcome(): boolean { - return this.scmService.repositories.length === 0; + return this.scmViewService.repositories.length === 0; } } diff --git a/src/vs/workbench/contrib/scm/browser/scmViewService.ts b/src/vs/workbench/contrib/scm/browser/scmViewService.ts index 5f718aacfa1..3c142e43909 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewService.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewService.ts @@ -51,35 +51,46 @@ export class SCMViewService implements ISCMViewService { this._onDidChangeVisibleRepositories.fire({ added, removed }); } + get repositories(): ISCMRepository[] { return this.scmService.repositories; } + + private _onDidAddRepository = new Emitter(); + readonly onDidAddRepository = this._onDidAddRepository.event; + + private _onDidRemoveRepository = new Emitter(); + readonly onDidRemoveRepository = this._onDidRemoveRepository.event; + private _onDidChangeVisibleRepositories = new Emitter(); readonly onDidChangeVisibleRepositories = this._onDidChangeVisibleRepositories.event; constructor( - @ISCMService scmService: ISCMService, + @ISCMService private readonly scmService: ISCMService, @IInstantiationService instantiationService: IInstantiationService ) { this.menus = instantiationService.createInstance(SCMMenus); - scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables); - scmService.onDidRemoveRepository(this.onDidRemoveRepository, this, this.disposables); + scmService.onDidAddRepository(this.onDidAddServiceRepository, this, this.disposables); + scmService.onDidRemoveRepository(this.onDidRemoveServiceRepository, this, this.disposables); for (const repository of scmService.repositories) { - this.onDidAddRepository(repository); + this.onDidAddServiceRepository(repository); } } - private onDidAddRepository(repository: ISCMRepository): void { + private onDidAddServiceRepository(repository: ISCMRepository): void { this._visibleRepositories.push(repository); this._visibleRepositoriesSet.add(repository); + + this._onDidAddRepository.fire(repository); this._onDidChangeVisibleRepositories.fire({ added: [repository], removed: Iterable.empty() }); } - private onDidRemoveRepository(repository: ISCMRepository): void { + private onDidRemoveServiceRepository(repository: ISCMRepository): void { const index = this._visibleRepositories.indexOf(repository); if (index > -1) { this._visibleRepositories.splice(index, 1); this._visibleRepositoriesSet.delete(repository); + this._onDidRemoveRepository.fire(repository); this._onDidChangeVisibleRepositories.fire({ added: Iterable.empty(), removed: [repository] }); } } diff --git a/src/vs/workbench/contrib/scm/common/scm.ts b/src/vs/workbench/contrib/scm/common/scm.ts index 9a123a3899f..8af31a87fc0 100644 --- a/src/vs/workbench/contrib/scm/common/scm.ts +++ b/src/vs/workbench/contrib/scm/common/scm.ts @@ -14,6 +14,7 @@ import { IMenu } from 'vs/platform/actions/common/actions'; export const VIEWLET_ID = 'workbench.view.scm'; export const VIEW_PANE_ID = 'workbench.scm'; +export const REPOSITORIES_VIEW_PANE_ID = 'workbench.scm.repositories'; export interface IBaselineResourceProvider { getBaselineResource(resource: URI): Promise; @@ -145,6 +146,11 @@ export interface ISCMViewVisibleRepositoryChangeEvent { export interface ISCMViewService { readonly _serviceBrand: undefined; + + readonly onDidAddRepository: Event; + readonly onDidRemoveRepository: Event; + readonly repositories: ISCMRepository[]; + readonly menus: ISCMMenus; visibleRepositories: ISCMRepository[]; readonly onDidChangeVisibleRepositories: Event; diff --git a/src/vs/workbench/contrib/scm/common/scmService.ts b/src/vs/workbench/contrib/scm/common/scmService.ts index fac8be966b7..6319bf1d441 100644 --- a/src/vs/workbench/contrib/scm/common/scmService.ts +++ b/src/vs/workbench/contrib/scm/common/scmService.ts @@ -7,6 +7,7 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { ISCMService, ISCMProvider, ISCMInput, ISCMRepository, IInputValidator } from './scm'; import { ILogService } from 'vs/platform/log/common/log'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; class SCMInput implements ISCMInput { @@ -113,6 +114,7 @@ export class SCMService implements ISCMService { private _repositories: ISCMRepository[] = []; get repositories(): ISCMRepository[] { return [...this._repositories]; } + private providerCount: IContextKey; private _selectedRepository: ISCMRepository | undefined; private readonly _onDidSelectRepository = new Emitter(); @@ -124,7 +126,12 @@ export class SCMService implements ISCMService { private readonly _onDidRemoveProvider = new Emitter(); readonly onDidRemoveRepository: Event = this._onDidRemoveProvider.event; - constructor(@ILogService private readonly logService: ILogService) { } + constructor( + @ILogService private readonly logService: ILogService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + this.providerCount = contextKeyService.createKey('scm.providerCount', 0); + } registerSCMProvider(provider: ISCMProvider): ISCMRepository { this.logService.trace('SCMService#registerSCMProvider'); @@ -150,6 +157,8 @@ export class SCMService implements ISCMService { if (this._selectedRepository === repository) { this.select(this._repositories[0]); } + + this.providerCount.set(this._repositories.length); }); const repository = new SCMRepository(provider, disposable); @@ -162,6 +171,7 @@ export class SCMService implements ISCMService { repository.setSelected(true); } + this.providerCount.set(this._repositories.length); return repository; } From d4d5780ba60a96aab4f9a651745d92226a9348fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Tue, 11 Aug 2020 16:09:33 +0200 Subject: [PATCH 112/736] polish scm repositories view pane --- .../scm/browser/scmRepositoriesViewPane.ts | 9 +-- .../contrib/scm/browser/scmViewPane.ts | 13 ++-- .../contrib/scm/browser/scmViewService.ts | 59 ++++++++++++------- src/vs/workbench/contrib/scm/common/scm.ts | 4 -- 4 files changed, 50 insertions(+), 35 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts index 638d743df38..f32780c5ee3 100644 --- a/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts @@ -8,7 +8,7 @@ import { localize } from 'vs/nls'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { append, $ } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; -import { ISCMRepository, ISCMViewService } from 'vs/workbench/contrib/scm/common/scm'; +import { ISCMRepository, ISCMService, ISCMViewService } from 'vs/workbench/contrib/scm/common/scm'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -41,6 +41,7 @@ export class SCMRepositoriesViewPane extends ViewPane { constructor( options: IViewPaneOptions, + @ISCMService protected scmService: ISCMService, @ISCMViewService protected scmViewService: ISCMViewService, @IKeybindingService protected keybindingService: IKeybindingService, @IContextMenuService protected contextMenuService: IContextMenuService, @@ -86,10 +87,10 @@ export class SCMRepositoriesViewPane extends ViewPane { this._register(this.scmViewService.onDidChangeVisibleRepositories(this.updateListSelection, this)); - this._register(this.scmViewService.onDidAddRepository(this.onDidAddRepository, this)); - this._register(this.scmViewService.onDidRemoveRepository(this.onDidRemoveRepository, this)); + this._register(this.scmService.onDidAddRepository(this.onDidAddRepository, this)); + this._register(this.scmService.onDidRemoveRepository(this.onDidRemoveRepository, this)); - for (const repository of this.scmViewService.repositories) { + for (const repository of this.scmService.repositories) { this.onDidAddRepository(repository); } diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index e0232e22d8d..1f46c0e6c23 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -10,7 +10,7 @@ import { IDisposable, Disposable, DisposableStore, combinedDisposable, dispose } import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { append, $, addClass, toggleClass, removeClass, Dimension } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { ISCMResourceGroup, ISCMResource, InputValidationType, ISCMRepository, ISCMInput, IInputValidation, ISCMViewService, ISCMViewVisibleRepositoryChangeEvent } from 'vs/workbench/contrib/scm/common/scm'; +import { ISCMResourceGroup, ISCMResource, InputValidationType, ISCMRepository, ISCMInput, IInputValidation, ISCMViewService, ISCMViewVisibleRepositoryChangeEvent, ISCMService } from 'vs/workbench/contrib/scm/common/scm'; import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -1516,6 +1516,7 @@ export class SCMViewPane extends ViewPane { constructor( options: IViewPaneOptions, + @ISCMService private scmService: ISCMService, @ISCMViewService private scmViewService: ISCMViewService, @IKeybindingService protected keybindingService: IKeybindingService, @IThemeService protected themeService: IThemeService, @@ -1533,7 +1534,7 @@ export class SCMViewPane extends ViewPane { @ITelemetryService telemetryService: ITelemetryService, ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); - this._register(Event.any(this.scmViewService.onDidAddRepository, this.scmViewService.onDidRemoveRepository)(() => this._onDidChangeViewWelcomeState.fire())); + this._register(Event.any(this.scmService.onDidAddRepository, this.scmService.onDidRemoveRepository)(() => this._onDidChangeViewWelcomeState.fire())); this._register(this.scmViewService.menus.titleMenu.onDidChangeTitle(this.updateActions, this)); } @@ -1725,12 +1726,12 @@ export class SCMViewPane extends ViewPane { return; } else if (isSCMResourceGroup(e.element)) { // TODO@joao: remove const provider = e.element.provider; - const repository = this.scmViewService.repositories.find(r => r.provider === provider); + const repository = this.scmService.repositories.find(r => r.provider === provider); repository?.setSelected(true); return; } else if (ResourceTree.isResourceNode(e.element)) { // TODO@joao: remove const provider = e.element.context.provider; - const repository = this.scmViewService.repositories.find(r => r.provider === provider); + const repository = this.scmService.repositories.find(r => r.provider === provider); repository?.setSelected(true); return; } else if (isSCMInput(e.element)) { @@ -1764,7 +1765,7 @@ export class SCMViewPane extends ViewPane { // TODO@joao: remove const provider = e.element.resourceGroup.provider; - const repository = this.scmViewService.repositories.find(r => r.provider === provider); + const repository = this.scmService.repositories.find(r => r.provider === provider); repository?.setSelected(true); } @@ -1825,7 +1826,7 @@ export class SCMViewPane extends ViewPane { } shouldShowWelcome(): boolean { - return this.scmViewService.repositories.length === 0; + return this.scmService.repositories.length === 0; } } diff --git a/src/vs/workbench/contrib/scm/browser/scmViewService.ts b/src/vs/workbench/contrib/scm/browser/scmViewService.ts index 3c142e43909..c9a1ac2154b 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewService.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { DisposableStore } from 'vs/base/common/lifecycle'; -import { Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { ISCMViewService, ISCMRepository, ISCMService, ISCMViewVisibleRepositoryChangeEvent, ISCMMenus } from 'vs/workbench/contrib/scm/common/scm'; import { Iterable } from 'vs/base/common/iterator'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -48,19 +48,26 @@ export class SCMViewService implements ISCMViewService { this._visibleRepositories = visibleRepositories; this._visibleRepositoriesSet = set; - this._onDidChangeVisibleRepositories.fire({ added, removed }); + this._onDidSetVisibleRepositories.fire({ added, removed }); } - get repositories(): ISCMRepository[] { return this.scmService.repositories; } + private _onDidChangeRepositories = new Emitter(); + private _onDidSetVisibleRepositories = new Emitter(); + readonly onDidChangeVisibleRepositories = Event.any( + this._onDidSetVisibleRepositories.event, + Event.debounce( + this._onDidChangeRepositories.event, + (last, e) => { + if (!last) { + return e; + } - private _onDidAddRepository = new Emitter(); - readonly onDidAddRepository = this._onDidAddRepository.event; - - private _onDidRemoveRepository = new Emitter(); - readonly onDidRemoveRepository = this._onDidRemoveRepository.event; - - private _onDidChangeVisibleRepositories = new Emitter(); - readonly onDidChangeVisibleRepositories = this._onDidChangeVisibleRepositories.event; + return { + added: Iterable.concat(last.added, e.added), + removed: Iterable.concat(last.removed, e.removed), + }; + }, 0) + ); constructor( @ISCMService private readonly scmService: ISCMService, @@ -68,35 +75,45 @@ export class SCMViewService implements ISCMViewService { ) { this.menus = instantiationService.createInstance(SCMMenus); - scmService.onDidAddRepository(this.onDidAddServiceRepository, this, this.disposables); - scmService.onDidRemoveRepository(this.onDidRemoveServiceRepository, this, this.disposables); + scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables); + scmService.onDidRemoveRepository(this.onDidRemoveRepository, this, this.disposables); for (const repository of scmService.repositories) { - this.onDidAddServiceRepository(repository); + this.onDidAddRepository(repository); } } - private onDidAddServiceRepository(repository: ISCMRepository): void { + private onDidAddRepository(repository: ISCMRepository): void { this._visibleRepositories.push(repository); this._visibleRepositoriesSet.add(repository); - this._onDidAddRepository.fire(repository); - this._onDidChangeVisibleRepositories.fire({ added: [repository], removed: Iterable.empty() }); + this._onDidChangeRepositories.fire({ added: [repository], removed: Iterable.empty() }); } - private onDidRemoveServiceRepository(repository: ISCMRepository): void { + private onDidRemoveRepository(repository: ISCMRepository): void { const index = this._visibleRepositories.indexOf(repository); if (index > -1) { + let added: Iterable = Iterable.empty(); + this._visibleRepositories.splice(index, 1); this._visibleRepositoriesSet.delete(repository); - this._onDidRemoveRepository.fire(repository); - this._onDidChangeVisibleRepositories.fire({ added: Iterable.empty(), removed: [repository] }); + + if (this._visibleRepositories.length === 0 && this.scmService.repositories.length > 0) { + const first = this.scmService.repositories[0]; + + this._visibleRepositories.push(first); + this._visibleRepositoriesSet.add(first); + added = [first]; + } + + this._onDidChangeRepositories.fire({ added, removed: [repository] }); } } dispose(): void { this.disposables.dispose(); - this._onDidChangeVisibleRepositories.dispose(); + this._onDidChangeRepositories.dispose(); + this._onDidSetVisibleRepositories.dispose(); } } diff --git a/src/vs/workbench/contrib/scm/common/scm.ts b/src/vs/workbench/contrib/scm/common/scm.ts index 8af31a87fc0..d3ebc93d9f7 100644 --- a/src/vs/workbench/contrib/scm/common/scm.ts +++ b/src/vs/workbench/contrib/scm/common/scm.ts @@ -147,10 +147,6 @@ export interface ISCMViewVisibleRepositoryChangeEvent { export interface ISCMViewService { readonly _serviceBrand: undefined; - readonly onDidAddRepository: Event; - readonly onDidRemoveRepository: Event; - readonly repositories: ISCMRepository[]; - readonly menus: ISCMMenus; visibleRepositories: ISCMRepository[]; readonly onDidChangeVisibleRepositories: Event; From 038573aa0f388efde0011f4909d9958bc96b656b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 11 Aug 2020 16:15:41 +0200 Subject: [PATCH 113/736] update notebooks --- .vscode/notebooks/api.github-issues | 2 +- .vscode/notebooks/inbox.github-issues | 9 ++++++--- .vscode/notebooks/my-work.github-issues | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index f36bb397581..ad2d028465e 100644 --- a/.vscode/notebooks/api.github-issues +++ b/.vscode/notebooks/api.github-issues @@ -8,7 +8,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"July 2020\"", + "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"August 2020\"", "editable": true }, { diff --git a/.vscode/notebooks/inbox.github-issues b/.vscode/notebooks/inbox.github-issues index 979da52c04d..e2e9e8a1cbb 100644 --- a/.vscode/notebooks/inbox.github-issues +++ b/.vscode/notebooks/inbox.github-issues @@ -8,17 +8,20 @@ { "kind": 2, "language": "github-issues", - "value": "$inbox=repo:microsoft/vscode is:open no:assignee -label:feature-request -label:testplan-item -label:plan-item " + "value": "$inbox=repo:microsoft/vscode is:open no:assignee -label:feature-request -label:testplan-item -label:plan-item ", + "editable": true }, { "kind": 1, "language": "markdown", - "value": "## Inbox tracking and Issue triage" + "value": "## Inbox tracking and Issue triage", + "editable": true }, { "kind": 1, "language": "markdown", - "value": "New issues or pull requests submitted by the community are initially triaged by an [automatic classification bot](https://github.com/microsoft/vscode-github-triage-actions/tree/master/classifier-deep). Issues that the bot does not correctly triage are then triaged by a team member. The team rotates the inbox tracker on a weekly basis.\n\nA [mirror](https://github.com/JacksonKearl/testissues/issues) of the VS Code issue stream is available with details about how the bot classifies issues, including feature-area classifications and confidence ratings. Per-category confidence thresholds and feature-area ownership data is maintained in [.github/classifier.json](https://github.com/microsoft/vscode/blob/master/.github/classifier.json). \n\n💡 The bot is being run through a GitHub action that runs every 30 minutes. Give the bot the opportunity to classify an issue before doing it manually.\n\n### Inbox Tracking\n\nThe inbox tracker is responsible for the [global inbox](https://github.com/Microsoft/vscode/issues?utf8=%E2%9C%93&q=is%3Aopen+no%3Aassignee+-label%3Afeature-request+-label%3Atestplan-item+-label%3Aplan-item) containing all **open issues and pull requests** that\n- are neither **feature requests** nor **test plan items** nor **plan items** and\n- have **no owner assignment**.\n\nThe **inbox tracker** may perform any step described in our [issue triaging documentation](https://github.com/microsoft/vscode/wiki/Issues-Triaging) but its main responsibility is to route issues to the actual feature area owner.\n\nFeature area owners track the **feature area inbox** containing all **open issues and pull requests** that\n- are personally assigned to them and are not assigned to any milestone\n- are labeled with their feature area label and are not assigned to any milestone.\nThis secondary triage may involve any of the steps described in our [issue triaging documentation](https://github.com/microsoft/vscode/wiki/Issues-Triaging) and results in a fully triaged or closed issue.\n\nThe [github triage extension](https://github.com/microsoft/vscode-github-triage-extension) can be used to assist with triaging — it provides a \"Command Palette\"-style list of triaging actions like assignment, labeling, and triggers for various bot actions." + "value": "New issues or pull requests submitted by the community are initially triaged by an [automatic classification bot](https://github.com/microsoft/vscode-github-triage-actions/tree/master/classifier-deep). Issues that the bot does not correctly triage are then triaged by a team member. The team rotates the inbox tracker on a weekly basis.\n\nA [mirror](https://github.com/JacksonKearl/testissues/issues) of the VS Code issue stream is available with details about how the bot classifies issues, including feature-area classifications and confidence ratings. Per-category confidence thresholds and feature-area ownership data is maintained in [.github/classifier.json](https://github.com/microsoft/vscode/blob/master/.github/classifier.json). \n\n💡 The bot is being run through a GitHub action that runs every 30 minutes. Give the bot the opportunity to classify an issue before doing it manually.\n\n### Inbox Tracking\n\nThe inbox tracker is responsible for the [global inbox](https://github.com/Microsoft/vscode/issues?utf8=%E2%9C%93&q=is%3Aopen+no%3Aassignee+-label%3Afeature-request+-label%3Atestplan-item+-label%3Aplan-item) containing all **open issues and pull requests** that\n- are neither **feature requests** nor **test plan items** nor **plan items** and\n- have **no owner assignment**.\n\nThe **inbox tracker** may perform any step described in our [issue triaging documentation](https://github.com/microsoft/vscode/wiki/Issues-Triaging) but its main responsibility is to route issues to the actual feature area owner.\n\nFeature area owners track the **feature area inbox** containing all **open issues and pull requests** that\n- are personally assigned to them and are not assigned to any milestone\n- are labeled with their feature area label and are not assigned to any milestone.\nThis secondary triage may involve any of the steps described in our [issue triaging documentation](https://github.com/microsoft/vscode/wiki/Issues-Triaging) and results in a fully triaged or closed issue.\n\nThe [github triage extension](https://github.com/microsoft/vscode-github-triage-extension) can be used to assist with triaging — it provides a \"Command Palette\"-style list of triaging actions like assignment, labeling, and triggers for various bot actions.", + "editable": true }, { "kind": 1, diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index dc6d33365d7..d1452276f6e 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -8,7 +8,7 @@ { "kind": 2, "language": "github-issues", - "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks\n\n// current milestone name\n$milestone=milestone:\"June 2020\"", + "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks\n\n// current milestone name\n$milestone=milestone:\"August 2020\"", "editable": true }, { From 9a043397f938c4de81a0b1b72c4f72b88921b70a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Tue, 11 Aug 2020 16:45:53 +0200 Subject: [PATCH 114/736] cleaner submenu action --- src/vs/base/browser/ui/menu/menu.ts | 3 +-- src/vs/base/browser/ui/toolbar/toolbar.ts | 3 +-- src/vs/base/common/actions.ts | 7 ++++++- src/vs/platform/actions/browser/menuEntryActionViewItem.ts | 2 +- .../contextmenu/electron-sandbox/contextmenuService.ts | 3 +-- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index a150e286875..f7e9bea946b 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -324,8 +324,7 @@ export class Menu extends ActionBar { if (action instanceof Separator) { return new MenuSeparatorActionViewItem(options.context, action, { icon: true }); } else if (action instanceof SubmenuAction) { - const actions = Array.isArray(action.actions) ? action.actions : action.actions(); - const menuActionViewItem = new SubmenuMenuActionViewItem(action, actions, parentData, { ...options, submenuIds: new Set([...(options.submenuIds || []), action.id]) }); + const menuActionViewItem = new SubmenuMenuActionViewItem(action, action.actions, parentData, { ...options, submenuIds: new Set([...(options.submenuIds || []), action.id]) }); if (options.enableMnemonics) { const mnemonic = menuActionViewItem.getMnemonic(); diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 4210cded15c..4b0908e8750 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -91,10 +91,9 @@ export class ToolBar extends Disposable { } if (action instanceof SubmenuAction) { - const actions = Array.isArray(action.actions) ? action.actions : action.actions(); const result = new DropdownMenuActionViewItem( action, - actions, + action.actions, contextMenuProvider, { actionViewItemProvider: this.options.actionViewItemProvider, diff --git a/src/vs/base/common/actions.ts b/src/vs/base/common/actions.ts index a3b396524eb..196aeda3945 100644 --- a/src/vs/base/common/actions.ts +++ b/src/vs/base/common/actions.ts @@ -237,7 +237,12 @@ export class Separator extends Action { export type SubmenuActions = IAction[] | (() => IAction[]); export class SubmenuAction extends Action { - constructor(id: string, label: string, readonly actions: SubmenuActions, cssClass?: string) { + + get actions(): IAction[] { + return Array.isArray(this._actions) ? this._actions : this._actions(); + } + + constructor(id: string, label: string, private _actions: SubmenuActions, cssClass?: string) { super(id, label, cssClass, true); } } diff --git a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts index d0867eb9d8d..0c84bbc7c63 100644 --- a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts +++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts @@ -303,6 +303,6 @@ export class SubmenuEntryActionViewItem extends DropdownMenuActionViewItem { } } - super(action, Array.isArray(action.actions) ? action.actions : action.actions(), _contextMenuService, { classNames }); + super(action, action.actions, _contextMenuService, { classNames }); } } diff --git a/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts index ee78a258ee3..861d178a11b 100644 --- a/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts +++ b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts @@ -142,10 +142,9 @@ class NativeContextMenuService extends Disposable implements IContextMenuService return undefined; } - const actions = Array.isArray(entry.actions) ? entry.actions : entry.actions(); return { label: unmnemonicLabel(stripCodicons(entry.label)).trim(), - submenu: this.createMenu(delegate, actions, onHide, new Set([...submenuIds, entry.id])) + submenu: this.createMenu(delegate, entry.actions, onHide, new Set([...submenuIds, entry.id])) }; } From 35359edc100815f7039f3cd4c6dee00311bb6f2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Tue, 11 Aug 2020 16:46:06 +0200 Subject: [PATCH 115/736] scm: repositories visibility --- .../contrib/scm/browser/scmViewPane.ts | 44 ++++++++++++++----- .../contrib/scm/browser/scmViewService.ts | 25 +++++++++++ src/vs/workbench/contrib/scm/browser/util.ts | 27 +++++++++++- src/vs/workbench/contrib/scm/common/scm.ts | 4 ++ 4 files changed, 88 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 1f46c0e6c23..1b621fc32bb 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -23,7 +23,7 @@ import { MenuItemAction, IMenuService } from 'vs/platform/actions/common/actions import { IAction, IActionViewItem, ActionRunner, Action, RadioGroup, Separator, SubmenuAction, IActionViewItemProvider } from 'vs/base/common/actions'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { IThemeService, LIGHT, registerThemingParticipant, IFileIconTheme } from 'vs/platform/theme/common/themeService'; -import { isSCMResource, isSCMResourceGroup, connectPrimaryMenuToInlineActionBar, isSCMRepository, isSCMInput, collectContextMenuActions, StatusBarAction, StatusBarActionViewItem } from './util'; +import { isSCMResource, isSCMResourceGroup, connectPrimaryMenuToInlineActionBar, isSCMRepository, isSCMInput, collectContextMenuActions, StatusBarAction, StatusBarActionViewItem, getRepositoryVisibilityActions } from './util'; import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; import { WorkbenchCompressibleObjectTree, IOpenEvent } from 'vs/platform/list/browser/listService'; import { IConfigurationService, ConfigurationTarget, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; @@ -750,6 +750,7 @@ class ViewModel { private inputRenderer: InputRenderer, private _mode: ViewModelMode, private _sortKey: ViewModelSortKey, + @IInstantiationService protected instantiationService: IInstantiationService, @IEditorService protected editorService: IEditorService, @IConfigurationService protected configurationService: IConfigurationService, @ISCMViewService private scmViewService: ISCMViewService @@ -1003,7 +1004,7 @@ class ViewModel { } if (!this.viewSubMenuAction) { - this.viewSubMenuAction = new SCMViewSubMenuAction(this); + this.viewSubMenuAction = this.instantiationService.createInstance(SCMViewSubMenuAction, this); this.disposables.add(this.viewSubMenuAction); } @@ -1073,25 +1074,43 @@ class ViewModel { } } +class SCMViewRepositoriesSubMenuAction extends SubmenuAction { + + get actions(): IAction[] { + return getRepositoryVisibilityActions(this.scmService, this.scmViewService); + } + + constructor( + @ISCMService private readonly scmService: ISCMService, + @ISCMViewService private readonly scmViewService: ISCMViewService, + ) { + super('scm.repositories', localize('repositories', "Repositories"), []); + } +} + class SCMViewSubMenuAction extends SubmenuAction { - readonly actions!: IAction[]; - - constructor(viewModel: ViewModel) { + constructor( + viewModel: ViewModel, + @IInstantiationService instantiationService: IInstantiationService + ) { const listAction = new SCMViewModeListAction(viewModel); const treeAction = new SCMViewModeTreeAction(viewModel); const sortByNameAction = new SCMSortByNameAction(viewModel); const sortByPathAction = new SCMSortByPathAction(viewModel); const sortByStatusAction = new SCMSortByStatusAction(viewModel); + const actions = [ + instantiationService.createInstance(SCMViewRepositoriesSubMenuAction), + new Separator(), + ...new RadioGroup([listAction, treeAction]).actions, + new Separator(), + ...new RadioGroup([sortByNameAction, sortByPathAction, sortByStatusAction]).actions + ]; super( 'scm.viewsort', localize('sortAction', "View & Sort"), - [ - ...new RadioGroup([listAction, treeAction]).actions, - new Separator(), - ...new RadioGroup([sortByNameAction, sortByPathAction, sortByStatusAction]).actions - ] + actions ); this._register(combinedDisposable(listAction, treeAction, sortByNameAction, sortByPathAction, sortByStatusAction)); @@ -1771,7 +1790,10 @@ export class SCMViewPane extends ViewPane { private onListContextMenu(e: ITreeContextMenuEvent): void { if (!e.element) { - return; + return this.contextMenuService.showContextMenu({ + getAnchor: () => e.anchor, + getActions: () => getRepositoryVisibilityActions(this.scmService, this.scmViewService) + }); } const element = e.element; diff --git a/src/vs/workbench/contrib/scm/browser/scmViewService.ts b/src/vs/workbench/contrib/scm/browser/scmViewService.ts index c9a1ac2154b..1f2609eb975 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewService.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewService.ts @@ -111,6 +111,31 @@ export class SCMViewService implements ISCMViewService { } } + isVisible(repository: ISCMRepository): boolean { + return this._visibleRepositoriesSet.has(repository); + } + + toggleVisibility(repository: ISCMRepository, visible?: boolean): void { + if (typeof visible === 'undefined') { + visible = !this.isVisible(repository); + } else if (this.isVisible(repository) === visible) { + return; + } + + if (visible) { + this.visibleRepositories = [...this.visibleRepositories, repository]; + } else { + const index = this.visibleRepositories.indexOf(repository); + + if (index > -1) { + this.visibleRepositories = [ + ...this.visibleRepositories.slice(0, index), + ...this.visibleRepositories.slice(index + 1) + ]; + } + } + } + dispose(): void { this.disposables.dispose(); this._onDidChangeRepositories.dispose(); diff --git a/src/vs/workbench/contrib/scm/browser/util.ts b/src/vs/workbench/contrib/scm/browser/util.ts index 228614a12d1..d8b489c5fce 100644 --- a/src/vs/workbench/contrib/scm/browser/util.ts +++ b/src/vs/workbench/contrib/scm/browser/util.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ISCMResource, ISCMRepository, ISCMResourceGroup, ISCMInput } from 'vs/workbench/contrib/scm/common/scm'; +import { ISCMResource, ISCMRepository, ISCMResourceGroup, ISCMInput, ISCMService, ISCMViewService } from 'vs/workbench/contrib/scm/common/scm'; import { IMenu } from 'vs/platform/actions/common/actions'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { IDisposable, Disposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -16,6 +16,8 @@ import { renderCodicons } from 'vs/base/common/codicons'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { Command } from 'vs/editor/common/modes'; import { escape } from 'vs/base/common/strings'; +import { basename } from 'vs/base/common/resources'; +import { Iterable } from 'vs/base/common/iterator'; export function isSCMRepository(element: any): element is ISCMRepository { return !!(element as ISCMRepository).provider && typeof (element as ISCMRepository).setSelected === 'function'; @@ -107,3 +109,26 @@ export class StatusBarActionViewItem extends ActionViewItem { } } } + +export function getRepositoryVisibilityActions(scmService: ISCMService, scmViewService: ISCMViewService): IAction[] { + const visible = new Set(); + const actions = scmService.repositories.map(repository => { + const label = repository.provider.rootUri ? basename(repository.provider.rootUri) : repository.provider.label; + const action = new Action('scm.repository.toggleVisibility', label, undefined, true, async () => { + scmViewService.toggleVisibility(repository); + }); + + if (scmViewService.isVisible(repository)) { + action.checked = true; + visible.add(action); + } + + return action; + }); + + if (visible.size === 1) { + Iterable.first(visible.values())!.enabled = false; + } + + return actions; +} diff --git a/src/vs/workbench/contrib/scm/common/scm.ts b/src/vs/workbench/contrib/scm/common/scm.ts index d3ebc93d9f7..aa3f57f7849 100644 --- a/src/vs/workbench/contrib/scm/common/scm.ts +++ b/src/vs/workbench/contrib/scm/common/scm.ts @@ -148,6 +148,10 @@ export interface ISCMViewService { readonly _serviceBrand: undefined; readonly menus: ISCMMenus; + visibleRepositories: ISCMRepository[]; readonly onDidChangeVisibleRepositories: Event; + + isVisible(repository: ISCMRepository): boolean; + toggleVisibility(repository: ISCMRepository, visible?: boolean): void; } From f43307aa935125060d9d5e83ddc0ba3751070597 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 11 Aug 2020 16:46:39 +0200 Subject: [PATCH 116/736] run to cursor: If the cursor is on a line different than the one the debugger is currently paused on, then send the breakpoint at column 0 on the line otherwise set it at the precise column fixes #102199 --- .../contrib/debug/browser/debugEditorActions.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index c7249d02612..2954fda0f33 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -144,7 +144,15 @@ export class RunToCursorAction extends EditorAction { const uri = editor.getModel().uri; const bpExists = !!(debugService.getModel().getBreakpoints({ column: position.column, lineNumber: position.lineNumber, uri }).length); if (!bpExists) { - const breakpoints = await debugService.addBreakpoints(uri, [{ lineNumber: position.lineNumber, column: position.column }], 'debugEditorActions.runToCursorAction'); + let column = 0; + const focusedStackFrame = debugService.getViewModel().focusedStackFrame; + if (focusedStackFrame && focusedStackFrame.source.uri.toString() === uri.toString() && focusedStackFrame.range.startLineNumber === position.lineNumber) { + // If the cursor is on a line different than the one the debugger is currently paused on, then send the breakpoint at column 0 on the line + // otherwise set it at the precise column #102199 + column = position.column; + } + + const breakpoints = await debugService.addBreakpoints(uri, [{ lineNumber: position.lineNumber, column }], 'debugEditorActions.runToCursorAction'); if (breakpoints && breakpoints.length) { breakpointToRemove = breakpoints[0]; } From 5f6d850a85e42614ba9eb421f7d4dc7f02e73c6e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 11 Aug 2020 16:48:10 +0200 Subject: [PATCH 117/736] don't resend onDidOpen event --- src/vs/workbench/api/common/extHostDocumentsAndEditors.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts b/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts index 00718254a0f..1518dc43d00 100644 --- a/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts +++ b/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts @@ -92,12 +92,11 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha data.versionId, data.isDirty )); + this._documents.set(resource, ref); + addedDocuments.push(ref.value); } ref.ref(); - - this._documents.set(resource, ref); - addedDocuments.push(ref.value); } } From d92c1a8aa7c097181ab04d3fc0365bc2923d12ec Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 11 Aug 2020 08:19:02 -0700 Subject: [PATCH 118/736] Disallow writes to pty larger than 50 Fixes #38137 --- .../contrib/terminal/node/terminalProcess.ts | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts b/src/vs/workbench/contrib/terminal/node/terminalProcess.ts index c59d62f463e..a007d9aa027 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts +++ b/src/vs/workbench/contrib/terminal/node/terminalProcess.ts @@ -18,6 +18,13 @@ import { findExecutable } from 'vs/workbench/contrib/terminal/node/terminalEnvir import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; +// Writing large amounts of data can be corrupted for some reason, after looking into this is +// appears to be a race condition around writing to the FD which may be based on how powerful the +// hardware is. The workaround for this is to space out when large amounts of data is being written +// to the terminal. See https://github.com/microsoft/vscode/issues/38137 +const WRITE_MAX_CHUNK_SIZE = 50; +const WRITE_INTERVAL_MS = 5; + export class TerminalProcess extends Disposable implements ITerminalChildProcess { private _exitCode: number | undefined; private _exitMessage: string | undefined; @@ -27,6 +34,8 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess private _processStartupComplete: Promise | undefined; private _isDisposed: boolean = false; private _titleInterval: NodeJS.Timer | null = null; + private _writeQueue: string[] = []; + private _writeTimeout: NodeJS.Timeout | undefined; private readonly _initialCwd: string; private readonly _ptyOptions: pty.IPtyForkOptions | pty.IWindowsPtyForkOptions; @@ -232,8 +241,37 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess if (this._isDisposed || !this._ptyProcess) { return; } + for (let i = 0; i <= Math.floor(data.length / WRITE_MAX_CHUNK_SIZE); i++) { + this._writeQueue.push(data.substr(i * WRITE_MAX_CHUNK_SIZE, WRITE_MAX_CHUNK_SIZE)); + } + this._startWrite(); + } + + private _startWrite(): void { + // Don't write if it's already queued of is there is nothing to write + if (this._writeTimeout !== undefined || this._writeQueue.length === 0) { + return; + } + + this._doWrite(); + + // Don't queue more writes if the queue is empty + if (this._writeQueue.length === 0) { + this._writeTimeout = undefined; + return; + } + + // Queue the next write + this._writeTimeout = setTimeout(() => { + this._writeTimeout = undefined; + this._startWrite(); + }, WRITE_INTERVAL_MS); + } + + private _doWrite(): void { + const data = this._writeQueue.shift()!; this._logService.trace('IPty#write', `${data.length} characters`); - this._ptyProcess.write(data); + this._ptyProcess!.write(data); } public resize(cols: number, rows: number): void { From 21e633cc63a58c7403dffbf242e1ce29416c2143 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 11 Aug 2020 17:26:19 +0200 Subject: [PATCH 119/736] Catch the potential 'disconnect' error - no need to show it to the user since the adapter is shutting down fixes #103105 --- src/vs/workbench/contrib/debug/browser/rawDebugSession.ts | 6 ++++-- .../workbench/contrib/debug/common/abstractDebugAdapter.ts | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts index a400dde3bd5..720ed115418 100644 --- a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts @@ -281,7 +281,7 @@ export class RawDebugSession implements IDisposable { if (this.capabilities.supportsTerminateRequest) { if (!this.terminated) { this.terminated = true; - return this.send('terminate', { restart }, undefined, 1000); + return this.send('terminate', { restart }, undefined, 2000); } return this.disconnect(restart); } @@ -499,7 +499,9 @@ export class RawDebugSession implements IDisposable { this.inShutdown = true; if (this.debugAdapter) { try { - await this.send('disconnect', { restart }, undefined, 1000); + await this.send('disconnect', { restart }, undefined, 2000); + } catch (e) { + // Catch the potential 'disconnect' error - no need to show it to the user since the adapter is shutting down } finally { this.stopAdapter(error); } diff --git a/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts b/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts index 93a76d52c1e..8b5ab068ee3 100644 --- a/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts +++ b/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts @@ -6,6 +6,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { IDebugAdapter } from 'vs/workbench/contrib/debug/common/debug'; import { timeout } from 'vs/base/common/async'; +import { localize } from 'vs/nls'; /** * Abstract implementation of the low level API for a debug adapter. @@ -88,7 +89,7 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter { request_seq: request.seq, success: false, command, - message: `timeout after ${timeout} ms` + message: localize('timeout', "Timeout after {0} ms for '{1}'", timeout, command) }; clb(err); } From 6445a9633d428e0879d172d4f392c542ba887768 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Tue, 11 Aug 2020 18:33:48 +0200 Subject: [PATCH 120/736] make proposed stopDebugging API final; fixes #101883 --- src/vs/vscode.d.ts | 6 ++++++ src/vs/vscode.proposed.d.ts | 9 --------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 1a3efbc86da..ca87d5f7227 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -11215,6 +11215,12 @@ declare module 'vscode' { */ export function startDebugging(folder: WorkspaceFolder | undefined, nameOrConfiguration: string | DebugConfiguration, parentSessionOrOptions?: DebugSession | DebugSessionOptions): Thenable; + /** + * Stop the given debug session or stop all debug sessions if session is omitted. + * @param session The [debug session](#DebugSession) to stop; if omitted all sessions are stopped. + */ + export function stopDebugging(session?: DebugSession): Thenable; + /** * Add breakpoints. * @param breakpoints The breakpoints to add. diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index cdbaf2293d4..2f8f0c0b82f 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -762,15 +762,6 @@ declare module 'vscode' { debugAdapterExecutable?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult; } - export namespace debug { - - /** - * Stop the given debug session or stop all debug sessions if session is omitted. - * @param session The [debug session](#DebugSession) to stop; if omitted all sessions are stopped. - */ - export function stopDebugging(session?: DebugSession): Thenable; - } - //#endregion //#region LogLevel: https://github.com/microsoft/vscode/issues/85992 From 250727b53254e872d5b64e33686d5720afe8882b Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 11 Aug 2020 19:04:57 +0200 Subject: [PATCH 121/736] Output: only change channel when proper channel id is passed fixes #103496 --- .../workbench/contrib/output/browser/output.contribution.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/output/browser/output.contribution.ts b/src/vs/workbench/contrib/output/browser/output.contribution.ts index ad427f0da42..9a131ab24ef 100644 --- a/src/vs/workbench/contrib/output/browser/output.contribution.ts +++ b/src/vs/workbench/contrib/output/browser/output.contribution.ts @@ -115,7 +115,10 @@ registerAction2(class extends Action2 { }); } async run(accessor: ServicesAccessor, channelId: string): Promise { - accessor.get(IOutputService).showChannel(channelId); + if (typeof channelId === 'string') { + // Sometimes the action is executed with no channelId parameter, then we should just ignore it #103496 + accessor.get(IOutputService).showChannel(channelId); + } } }); registerAction2(class extends Action2 { From f6f8735825ffec761b0fb3c99aa9810d46e0a6a6 Mon Sep 17 00:00:00 2001 From: Aditya Thakral <9at8@users.noreply.github.com> Date: Tue, 11 Aug 2020 13:38:06 -0400 Subject: [PATCH 122/736] Silently ignore the screen size is bogus error in the Process Explorer, fixes ##98590 --- src/vs/base/node/ps.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/base/node/ps.ts b/src/vs/base/node/ps.ts index 55c3a73da57..0786b753dd2 100644 --- a/src/vs/base/node/ps.ts +++ b/src/vs/base/node/ps.ts @@ -219,7 +219,8 @@ export function listProcesses(rootPid: number): Promise { // Set numeric locale to ensure '.' is used as the decimal separator exec(`${ps} ${args}`, { maxBuffer: 1000 * 1024, env: { LC_NUMERIC: 'en_US.UTF-8' } }, (err, stdout, stderr) => { - if (err || stderr) { + // Silently ignoring the screen size is bogus error. See https://github.com/microsoft/vscode/issues/98590 + if (err || (stderr && !stderr.includes('screen size is bogus'))) { reject(err || new Error(stderr.toString())); } else { parsePsOutput(stdout, addToTree); @@ -246,4 +247,4 @@ function parsePsOutput(stdout: string, addToTree: (pid: number, ppid: number, cm addToTree(parseInt(matches[1]), parseInt(matches[2]), matches[5], parseFloat(matches[3]), parseFloat(matches[4])); } } -} \ No newline at end of file +} From c47927627cdcd7f1f0c90e38f1441bd803c29369 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Tue, 11 Aug 2020 10:42:16 -0700 Subject: [PATCH 123/736] fixes #95888 --- .../browser/parts/views/viewPaneContainer.ts | 79 +++++++++++++++++-- 1 file changed, 71 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index 028a1594494..fa81110dca1 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -606,6 +606,8 @@ const enum DropDirection { RIGHT } +type BoundingRect = { top: number, left: number, bottom: number, right: number }; + class ViewPaneDropOverlay extends Themable { private static readonly OVERLAY_ID = 'monaco-workbench-pane-drop-overlay'; @@ -627,6 +629,7 @@ class ViewPaneDropOverlay extends Themable { constructor( private paneElement: HTMLElement, private orientation: Orientation | undefined, + private bounds: BoundingRect | undefined, protected location: ViewContainerLocation, protected themeService: IThemeService, ) { @@ -758,7 +761,22 @@ class ViewPaneDropOverlay extends Themable { this.doPositionOverlay({ top: '0', right: '0', width: '50%', height: '100%' }); break; default: - this.doPositionOverlay({ top: '0', left: '0', width: '100%', height: '100%' }); + // const top = this.bounds?.top || 0; + // const left = this.bounds?.bottom || 0; + + let top = '0'; + let left = '0'; + let width = '100%'; + let height = '100%'; + if (this.bounds) { + const boundingRect = this.container.getBoundingClientRect(); + top = `${this.bounds.top - boundingRect.top}px`; + left = `${this.bounds.left - boundingRect.left}px`; + height = `${this.bounds.bottom - this.bounds.top}px`; + width = `${this.bounds.right - this.bounds.left}px`; + } + + this.doPositionOverlay({ top, left, width, height }); } if ((this.orientation === Orientation.VERTICAL && paneHeight <= 25) || @@ -899,9 +917,35 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e)))); let overlay: ViewPaneDropOverlay | undefined; + const getOverlayBounds: () => BoundingRect = () => { + const fullSize = parent.getBoundingClientRect(); + const lastPane = this.panes[this.panes.length - 1].element.getBoundingClientRect(); + const top = this.orientation === Orientation.VERTICAL ? lastPane.bottom : fullSize.top; + const left = this.orientation === Orientation.HORIZONTAL ? lastPane.right : fullSize.left; + + return { + top, + bottom: fullSize.bottom, + left, + right: fullSize.right, + }; + }; + + const inBounds = (bounds: BoundingRect, pos: { x: number, y: number }) => { + return pos.x >= bounds.left && pos.x <= bounds.right && pos.y >= bounds.top && pos.y <= bounds.bottom; + }; + + + let bounds: BoundingRect; + this._register(CompositeDragAndDropObserver.INSTANCE.registerTarget(parent, { onDragEnter: (e) => { - if (!overlay && this.panes.length === 0) { + bounds = getOverlayBounds(); + if (overlay && overlay.disposed) { + overlay = undefined; + } + + if (!overlay && inBounds(bounds, e.eventData)) { const dropData = e.dragAndDropData.getData(); if (dropData.type === 'view') { @@ -912,7 +956,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { return; } - overlay = new ViewPaneDropOverlay(parent, undefined, this.viewDescriptorService.getViewContainerLocation(this.viewContainer)!, this.themeService); + overlay = new ViewPaneDropOverlay(parent, undefined, bounds, this.viewDescriptorService.getViewContainerLocation(this.viewContainer)!, this.themeService); } if (dropData.type === 'composite' && dropData.id !== this.viewContainer.id) { @@ -920,14 +964,22 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { const viewsToMove = this.viewDescriptorService.getViewContainerModel(container).allViewDescriptors; if (!viewsToMove.some(v => !v.canMoveView) && viewsToMove.length > 0) { - overlay = new ViewPaneDropOverlay(parent, undefined, this.viewDescriptorService.getViewContainerLocation(this.viewContainer)!, this.themeService); + overlay = new ViewPaneDropOverlay(parent, undefined, bounds, this.viewDescriptorService.getViewContainerLocation(this.viewContainer)!, this.themeService); } } - } }, onDragOver: (e) => { - if (this.panes.length === 0) { + if (overlay && overlay.disposed) { + overlay = undefined; + } + + if (overlay && !inBounds(bounds, e.eventData)) { + overlay.dispose(); + overlay = undefined; + } + + if (inBounds(bounds, e.eventData)) { toggleDropEffect(e.eventData.dataTransfer, 'move', overlay !== undefined); } }, @@ -954,9 +1006,20 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { } } + const paneCount = this.panes.length; + if (viewsToMove.length > 0) { this.viewDescriptorService.moveViewsToContainer(viewsToMove, this.viewContainer); } + + if (paneCount > 0) { + for (const view of viewsToMove) { + const paneToMove = this.panes.find(p => p.id === view.id); + if (paneToMove) { + this.movePane(paneToMove, this.panes[this.panes.length - 1]); + } + } + } } overlay?.dispose(); @@ -1366,7 +1429,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { return; } - overlay = new ViewPaneDropOverlay(pane.dropTargetElement, this.orientation ?? Orientation.VERTICAL, this.viewDescriptorService.getViewContainerLocation(this.viewContainer)!, this.themeService); + overlay = new ViewPaneDropOverlay(pane.dropTargetElement, this.orientation ?? Orientation.VERTICAL, undefined, this.viewDescriptorService.getViewContainerLocation(this.viewContainer)!, this.themeService); } if (dropData.type === 'composite' && dropData.id !== this.viewContainer.id && !this.viewContainer.rejectAddedViews) { @@ -1374,7 +1437,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { const viewsToMove = this.viewDescriptorService.getViewContainerModel(container).allViewDescriptors; if (!viewsToMove.some(v => !v.canMoveView) && viewsToMove.length > 0) { - overlay = new ViewPaneDropOverlay(pane.dropTargetElement, this.orientation ?? Orientation.VERTICAL, this.viewDescriptorService.getViewContainerLocation(this.viewContainer)!, this.themeService); + overlay = new ViewPaneDropOverlay(pane.dropTargetElement, this.orientation ?? Orientation.VERTICAL, undefined, this.viewDescriptorService.getViewContainerLocation(this.viewContainer)!, this.themeService); } } } From d4972e22a65eb294f90a93b90d3a375196f8cdd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Tue, 11 Aug 2020 20:00:16 +0200 Subject: [PATCH 124/736] fix size constraints --- .../scm/browser/scmRepositoriesViewPane.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts index f32780c5ee3..503dd541418 100644 --- a/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts @@ -23,6 +23,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { RepositoryRenderer } from 'vs/workbench/contrib/scm/browser/scmRepositoryRenderer'; import { collectContextMenuActions, StatusBarAction, StatusBarActionViewItem } from 'vs/workbench/contrib/scm/browser/util'; +import { Orientation } from 'vs/base/browser/ui/sash/sash'; class ListDelegate implements IListVirtualDelegate { @@ -94,11 +95,13 @@ export class SCMRepositoriesViewPane extends ViewPane { this.onDidAddRepository(repository); } - this._register(this.configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('scm.repositories.visible')) { - this.updateBodySize(); - } - })); + if (this.orientation === Orientation.VERTICAL) { + this._register(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('scm.repositories.visible')) { + this.updateBodySize(); + } + })); + } this.updateListSelection(); } @@ -128,6 +131,10 @@ export class SCMRepositoriesViewPane extends ViewPane { } private updateBodySize(): void { + if (this.orientation === Orientation.HORIZONTAL) { + return; + } + const visibleCount = this.configurationService.getValue('scm.repositories.visible'); const empty = this.list.length === 0; const size = Math.min(this.list.length, visibleCount) * 22; From e4296330de2ae1ba69e8ecaacf6588f37a1bd639 Mon Sep 17 00:00:00 2001 From: Robo Date: Tue, 21 Jul 2020 00:39:34 -0700 Subject: [PATCH 125/736] chore: update to electron 9-x-y (#102011) Co-authored-by: Benjamin Pasero --- .yarnrc | 2 +- .../darwin/continuous-build-darwin.yml | 2 +- .../darwin/product-build-darwin.yml | 2 +- build/azure-pipelines/distro-build.yml | 2 +- build/azure-pipelines/exploration-build.yml | 2 +- .../linux/continuous-build-linux.yml | 2 +- .../linux/product-build-linux-multiarch.yml | 2 +- .../linux/product-build-linux.yml | 2 +- .../linux/snap-build-linux.yml | 2 +- build/azure-pipelines/product-compile.yml | 2 +- .../publish-types/publish-types.yml | 2 +- build/azure-pipelines/sync-mooncake.yml | 2 +- .../azure-pipelines/web/product-build-web.yml | 2 +- .../win32/continuous-build-win32.yml | 2 +- .../win32/product-build-win32-arm64.yml | 2 +- .../win32/product-build-win32.yml | 2 +- build/lib/i18n.resources.json | 4 - cgmanifest.json | 12 +-- package.json | 2 +- remote/.yarnrc | 2 +- src/main.js | 68 ++++++++------ .../contextmenu/electron-main/contextmenu.ts | 3 +- .../parts/sandbox/common/electronTypes.ts | 90 ++++++++++++------- .../parts/sandbox/electron-browser/preload.js | 9 +- .../parts/sandbox/electron-sandbox/globals.ts | 39 +++----- src/vs/code/electron-main/app.ts | 39 ++++++-- src/vs/platform/electron/common/electron.ts | 3 +- .../electron-main/electronMainService.ts | 12 +-- src/vs/platform/environment/node/argv.ts | 2 + .../environment/node/environmentService.ts | 2 +- .../launch/electron-main/launchMainService.ts | 16 ++-- src/vs/platform/storage/node/storageIpc.ts | 12 +-- src/vs/platform/telemetry/common/telemetry.ts | 1 - .../browser/relauncher.contribution.ts | 8 -- .../browser/telemetryOptOut.ts | 14 ++- .../electron-sandbox/telemetryOptOut.ts | 6 +- src/vs/workbench/electron-browser/window.ts | 69 ++------------ .../electron-sandbox/desktop.contribution.ts | 8 ++ .../electron-browser/environmentService.ts | 2 +- .../localProcessExtensionHost.ts | 22 +++-- .../electron-browser/workbenchTestServices.ts | 1 - test/unit/electron/index.js | 4 + yarn.lock | 8 +- 43 files changed, 249 insertions(+), 241 deletions(-) diff --git a/.yarnrc b/.yarnrc index 135e10442a7..68cb12c1284 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://atom.io/download/electron" -target "7.3.2" +target "9.2.0" runtime "electron" diff --git a/build/azure-pipelines/darwin/continuous-build-darwin.yml b/build/azure-pipelines/darwin/continuous-build-darwin.yml index 447e18e7cb5..631b9af7f10 100644 --- a/build/azure-pipelines/darwin/continuous-build-darwin.yml +++ b/build/azure-pipelines/darwin/continuous-build-darwin.yml @@ -1,7 +1,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index e231ca3d4f2..58353f1f283 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -21,7 +21,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: diff --git a/build/azure-pipelines/distro-build.yml b/build/azure-pipelines/distro-build.yml index 4689451b54e..f9bdf7fef8e 100644 --- a/build/azure-pipelines/distro-build.yml +++ b/build/azure-pipelines/distro-build.yml @@ -8,7 +8,7 @@ pr: steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: AzureKeyVault@1 displayName: 'Azure Key Vault: Get Secrets' diff --git a/build/azure-pipelines/exploration-build.yml b/build/azure-pipelines/exploration-build.yml index 370c56fa6a1..a8747353c37 100644 --- a/build/azure-pipelines/exploration-build.yml +++ b/build/azure-pipelines/exploration-build.yml @@ -11,7 +11,7 @@ pr: steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: AzureKeyVault@1 displayName: 'Azure Key Vault: Get Secrets' diff --git a/build/azure-pipelines/linux/continuous-build-linux.yml b/build/azure-pipelines/linux/continuous-build-linux.yml index 3e239caad54..41225110f37 100644 --- a/build/azure-pipelines/linux/continuous-build-linux.yml +++ b/build/azure-pipelines/linux/continuous-build-linux.yml @@ -10,7 +10,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: diff --git a/build/azure-pipelines/linux/product-build-linux-multiarch.yml b/build/azure-pipelines/linux/product-build-linux-multiarch.yml index 485f8dcfba7..258f87ea3d2 100644 --- a/build/azure-pipelines/linux/product-build-linux-multiarch.yml +++ b/build/azure-pipelines/linux/product-build-linux-multiarch.yml @@ -21,7 +21,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index b164e915d7e..ea6c0195954 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -21,7 +21,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: diff --git a/build/azure-pipelines/linux/snap-build-linux.yml b/build/azure-pipelines/linux/snap-build-linux.yml index a530499b313..39c39e86c9e 100644 --- a/build/azure-pipelines/linux/snap-build-linux.yml +++ b/build/azure-pipelines/linux/snap-build-linux.yml @@ -1,7 +1,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index db6524be03b..ab0dbb932c6 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -16,7 +16,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 diff --git a/build/azure-pipelines/publish-types/publish-types.yml b/build/azure-pipelines/publish-types/publish-types.yml index b73cd04a966..10b6aa4e16a 100644 --- a/build/azure-pipelines/publish-types/publish-types.yml +++ b/build/azure-pipelines/publish-types/publish-types.yml @@ -9,7 +9,7 @@ pr: none steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: diff --git a/build/azure-pipelines/sync-mooncake.yml b/build/azure-pipelines/sync-mooncake.yml index 2641830a413..49dfc9ced80 100644 --- a/build/azure-pipelines/sync-mooncake.yml +++ b/build/azure-pipelines/sync-mooncake.yml @@ -1,7 +1,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index 0c338203b4d..7f4907aa2d9 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -21,7 +21,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: diff --git a/build/azure-pipelines/win32/continuous-build-win32.yml b/build/azure-pipelines/win32/continuous-build-win32.yml index 1be638a4794..8600377139c 100644 --- a/build/azure-pipelines/win32/continuous-build-win32.yml +++ b/build/azure-pipelines/win32/continuous-build-win32.yml @@ -1,7 +1,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: diff --git a/build/azure-pipelines/win32/product-build-win32-arm64.yml b/build/azure-pipelines/win32/product-build-win32-arm64.yml index 01be34aa9a8..ecb50ad678e 100644 --- a/build/azure-pipelines/win32/product-build-win32-arm64.yml +++ b/build/azure-pipelines/win32/product-build-win32-arm64.yml @@ -21,7 +21,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 1c958ddd188..779bc8a8d57 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -21,7 +21,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 9bba404c243..ceef664b85f 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -246,10 +246,6 @@ "name": "vs/workbench/services/configurationResolver", "project": "vscode-workbench" }, - { - "name": "vs/workbench/services/crashReporter", - "project": "vscode-workbench" - }, { "name": "vs/workbench/services/dialogs", "project": "vscode-workbench" diff --git a/cgmanifest.json b/cgmanifest.json index cb9954628dd..576724e75af 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "chromium", "repositoryUrl": "https://chromium.googlesource.com/chromium/src", - "commitHash": "e4745133a1d3745f066e068b8033c6a269b59caf" + "commitHash": "894fb9eb56c6cbda65e3c3ae9ada6d4cb5850cc9" } }, "licenseDetail": [ @@ -40,7 +40,7 @@ "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ], "isOnlyProductionDependency": true, - "version": "78.0.3904.130" + "version": "83.0.4103.122" }, { "component": { @@ -48,11 +48,11 @@ "git": { "name": "nodejs", "repositoryUrl": "https://github.com/nodejs/node", - "commitHash": "787378879acfb212ed4ff824bf9f767a24a5cb43a" + "commitHash": "9622fed3fb2cffcea9efff6c8cb4cc2def99d75d" } }, "isOnlyProductionDependency": true, - "version": "12.8.1" + "version": "12.14.1" }, { "component": { @@ -60,12 +60,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "5f93e889020d279d5a9cd1ecab080ab467312447" + "commitHash": "0c2cb59b6283fe8d6bb4b14f8a832e2166aeaa0c" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "7.3.2" + "version": "9.2.0" }, { "component": { diff --git a/package.json b/package.json index ddd7346cafb..302de8a74d2 100644 --- a/package.json +++ b/package.json @@ -109,7 +109,7 @@ "css-loader": "^3.2.0", "debounce": "^1.0.0", "deemon": "^1.4.0", - "electron": "7.3.2", + "electron": "9.2.0", "eslint": "6.8.0", "eslint-plugin-jsdoc": "^19.1.0", "event-stream": "3.3.4", diff --git a/remote/.yarnrc b/remote/.yarnrc index 1e16cde724c..c1a32ce532a 100644 --- a/remote/.yarnrc +++ b/remote/.yarnrc @@ -1,3 +1,3 @@ disturl "http://nodejs.org/dist" -target "12.4.0" +target "12.14.1" runtime "node" diff --git a/src/main.js b/src/main.js index f179a672031..9a49d06217a 100644 --- a/src/main.js +++ b/src/main.js @@ -18,7 +18,11 @@ const bootstrap = require('./bootstrap'); const paths = require('./paths'); /** @type {any} */ const product = require('../product.json'); -const { app, protocol } = require('electron'); +const { app, protocol, crashReporter } = require('electron'); + +// Disable render process reuse, we still have +// non-context aware native modules in the renderer. +app.allowRendererProcessReuse = false; // Enable portable support const portable = bootstrap.configurePortable(product); @@ -31,13 +35,13 @@ const args = parseCLIArgs(); const userDataPath = getUserDataPath(args); app.setPath('userData', userDataPath); -// Set temp directory based on crash-reporter-directory CLI argument -// The crash reporter will store crashes in temp folder so we need -// to change that location accordingly. +// Configure static command line arguments +const argvConfig = configureCommandlineSwitchesSync(args); -// If a crash-reporter-directory is specified we setup the crash reporter -// right from the beginning as early as possible to monitor all processes. +// If a crash-reporter-directory is specified we store the crash reports +// in the specified directory and don't upload them to the crash server. let crashReporterDirectory = args['crash-reporter-directory']; +let submitURL = ''; if (crashReporterDirectory) { crashReporterDirectory = path.normalize(crashReporterDirectory); @@ -55,23 +59,41 @@ if (crashReporterDirectory) { } } - // Crashes are stored in the temp directory by default, so we + // Crashes are stored in the crashDumps directory by default, so we // need to change that directory to the provided one - console.log(`Found --crash-reporter-directory argument. Setting temp directory to be '${crashReporterDirectory}'`); - app.setPath('temp', crashReporterDirectory); - - // Start crash reporter - const { crashReporter } = require('electron'); - const productName = (product.crashReporter && product.crashReporter.productName) || product.nameShort; - const companyName = (product.crashReporter && product.crashReporter.companyName) || 'Microsoft'; - crashReporter.start({ - companyName: companyName, - productName: process.env['VSCODE_DEV'] ? `${productName} Dev` : productName, - submitURL: '', - uploadToServer: false - }); + console.log(`Found --crash-reporter-directory argument. Setting crashDumps directory to be '${crashReporterDirectory}'`); + app.setPath('crashDumps', crashReporterDirectory); +} else { + const appCenter = product.appCenter; + // Disable Appcenter crash reporting if + // * --crash-reporter-directory is specified + // * enable-crash-reporter runtime argument is set to 'false' + // * --disable-crash-reporter command line parameter is set + if (appCenter && argvConfig['enable-crash-reporter'] && !args['disable-crash-reporter']) { + const isWindows = (process.platform === 'win32'); + const isLinux = (process.platform === 'linux'); + const crashReporterId = argvConfig['crash-reporter-id']; + const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + if (uuidPattern.test(crashReporterId)) { + submitURL = isWindows ? appCenter[process.arch === 'ia32' ? 'win32-ia32' : 'win32-x64'] : isLinux ? appCenter[`linux-x64`] : appCenter.darwin; + submitURL = submitURL.concat('&uid=', crashReporterId, '&iid=', crashReporterId, '&sid=', crashReporterId); + // Send the id for child node process that are explicitly starting crash reporter. + // For vscode this is ExtensionHost process currently. + process.argv.push('--crash-reporter-id', crashReporterId); + } + } } +// Start crash reporter for all processes +const productName = (product.crashReporter ? product.crashReporter.productName : undefined) || product.nameShort; +const companyName = (product.crashReporter ? product.crashReporter.companyName : undefined) || 'Microsoft'; +crashReporter.start({ + companyName: companyName, + productName: process.env['VSCODE_DEV'] ? `${productName} Dev` : productName, + submitURL, + uploadToServer: !crashReporterDirectory +}); + // Set logs path before app 'ready' event if running portable // to ensure that no 'logs' folder is created on disk at a // location outside of the portable directory @@ -110,9 +132,6 @@ registerListeners(); // Cached data const nodeCachedDataDir = getNodeCachedDir(); -// Configure static command line arguments -const argvConfig = configureCommandlineSwitchesSync(args); - // Remove env set by snap https://github.com/microsoft/vscode/issues/85344 if (process.env['SNAP']) { delete process.env['GDK_PIXBUF_MODULE_FILE']; @@ -254,9 +273,6 @@ function configureCommandlineSwitchesSync(cliArgs) { app.commandLine.appendSwitch('js-flags', jsFlags); } - // TODO@Deepak Electron 7 workaround for https://github.com/microsoft/vscode/issues/88873 - app.commandLine.appendSwitch('disable-features', 'LayoutNG'); - return argvConfig; } diff --git a/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts index 6222be78028..a7fbfc81e6b 100644 --- a/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts +++ b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts @@ -5,13 +5,14 @@ import { Menu, MenuItem, BrowserWindow, ipcMain, IpcMainEvent } from 'electron'; import { ISerializableContextMenuItem, CONTEXT_MENU_CLOSE_CHANNEL, CONTEXT_MENU_CHANNEL, IPopupOptions } from 'vs/base/parts/contextmenu/common/contextmenu'; +import { withNullAsUndefined } from 'vs/base/common/types'; export function registerContextMenuListener(): void { ipcMain.on(CONTEXT_MENU_CHANNEL, (event: IpcMainEvent, contextMenuId: number, items: ISerializableContextMenuItem[], onClickChannel: string, options?: IPopupOptions) => { const menu = createMenu(event, onClickChannel, items); menu.popup({ - window: BrowserWindow.fromWebContents(event.sender), + window: withNullAsUndefined(BrowserWindow.fromWebContents(event.sender)), x: options ? options.x : undefined, y: options ? options.y : undefined, positioningItem: options ? options.positioningItem : undefined, diff --git a/src/vs/base/parts/sandbox/common/electronTypes.ts b/src/vs/base/parts/sandbox/common/electronTypes.ts index 8a5f4120862..c7729f338af 100644 --- a/src/vs/base/parts/sandbox/common/electronTypes.ts +++ b/src/vs/base/parts/sandbox/common/electronTypes.ts @@ -209,37 +209,6 @@ export interface SaveDialogReturnValue { bookmark?: string; } -export interface CrashReporterStartOptions { - companyName: string; - /** - * URL that crash reports will be sent to as POST. - */ - submitURL: string; - /** - * Defaults to `app.name`. - */ - productName?: string; - /** - * Whether crash reports should be sent to the server. Default is `true`. - */ - uploadToServer?: boolean; - /** - * Default is `false`. - */ - ignoreSystemCrashHandler?: boolean; - /** - * An object you can define that will be sent along with the report. Only string - * properties are sent correctly. Nested objects are not supported. When using - * Windows, the property names and values must be fewer than 64 characters. - */ - extra?: Record; - /** - * Directory to store the crash reports temporarily (only used when the crash - * reporter is started via `process.crashReporter.start`). - */ - crashesDirectory?: string; -} - export interface FileFilter { // Docs: http://electronjs.org/docs/api/structures/file-filter @@ -281,3 +250,62 @@ export interface MouseInputEvent extends InputEvent { x: number; y: number; } + +export interface CrashReporterStartOptions { + /** + * URL that crash reports will be sent to as POST. + */ + submitURL: string; + /** + * Defaults to `app.name`. + */ + productName?: string; + /** + * Deprecated alias for `{ globalExtra: { _companyName: ... } }`. + * + * @deprecated + */ + companyName?: string; + /** + * Whether crash reports should be sent to the server. If false, crash reports will + * be collected and stored in the crashes directory, but not uploaded. Default is + * `true`. + */ + uploadToServer?: boolean; + /** + * If true, crashes generated in the main process will not be forwarded to the + * system crash handler. Default is `false`. + */ + ignoreSystemCrashHandler?: boolean; + /** + * If true, limit the number of crashes uploaded to 1/hour. Default is `false`. + * + * @platform darwin,win32 + */ + rateLimit?: boolean; + /** + * If true, crash reports will be compressed and uploaded with `Content-Encoding: + * gzip`. Not all collection servers support compressed payloads. Default is + * `false`. + * + * @platform darwin,win32 + */ + compress?: boolean; + /** + * Extra string key/value annotations that will be sent along with crash reports + * that are generated in the main process. Only string values are supported. + * Crashes generated in child processes will not contain these extra parameters to + * crash reports generated from child processes, call `addExtraParameter` from the + * child process. + */ + extra?: Record; + /** + * Extra string key/value annotations that will be sent along with any crash + * reports generated in any process. These annotations cannot be changed once the + * crash reporter has been started. If a key is present in both the global extra + * parameters and the process-specific extra parameters, then the global one will + * take precedence. By default, `productName` and the app version are included, as + * well as the Electron version. + */ + globalExtra?: Record; +} diff --git a/src/vs/base/parts/sandbox/electron-browser/preload.js b/src/vs/base/parts/sandbox/electron-browser/preload.js index d10c4be3ae1..4dbe1b48a1d 100644 --- a/src/vs/base/parts/sandbox/electron-browser/preload.js +++ b/src/vs/base/parts/sandbox/electron-browser/preload.js @@ -74,15 +74,16 @@ }, /** - * Support for subset of methods of Electron's `crashReporter` type. + * Support for subset of methods of Electron's `crashReporter` type. */ crashReporter: { /** - * @param {Electron.CrashReporterStartOptions} options + * @param {string} key + * @param {string} value */ - start(options) { - crashReporter.start(options); + addExtraParameter(key, value) { + crashReporter.addExtraParameter(key, value); } }, diff --git a/src/vs/base/parts/sandbox/electron-sandbox/globals.ts b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts index 573e4735933..0acd55cb3fe 100644 --- a/src/vs/base/parts/sandbox/electron-sandbox/globals.ts +++ b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CrashReporterStartOptions } from 'vs/base/parts/sandbox/common/electronTypes'; - export const ipcRenderer = (window as any).vscode.ipcRenderer as { /** @@ -54,32 +52,23 @@ export const webFrame = (window as any).vscode.webFrame as { export const crashReporter = (window as any).vscode.crashReporter as { /** - * You are required to call this method before using any other `crashReporter` APIs - * and in each process (main/renderer) from which you want to collect crash - * reports. You can pass different options to `crashReporter.start` when calling - * from different processes. + * Set an extra parameter to be sent with the crash report. The values specified + * here will be sent in addition to any values set via the `extra` option when + * `start` was called. * - * **Note** Child processes created via the `child_process` module will not have - * access to the Electron modules. Therefore, to collect crash reports from them, - * use `process.crashReporter.start` instead. Pass the same options as above along - * with an additional one called `crashesDirectory` that should point to a - * directory to store the crash reports temporarily. You can test this out by - * calling `process.crash()` to crash the child process. + * Parameters added in this fashion (or via the `extra` parameter to + * `crashReporter.start`) are specific to the calling process. Adding extra + * parameters in the main process will not cause those parameters to be sent along + * with crashes from renderer or other child processes. Similarly, adding extra + * parameters in a renderer process will not result in those parameters being sent + * with crashes that occur in other renderer processes or in the main process. * - * **Note:** If you need send additional/updated `extra` parameters after your - * first call `start` you can call `addExtraParameter` on macOS or call `start` - * again with the new/updated `extra` parameters on Linux and Windows. - * - * **Note:** On macOS and windows, Electron uses a new `crashpad` client for crash - * collection and reporting. If you want to enable crash reporting, initializing - * `crashpad` from the main process using `crashReporter.start` is required - * regardless of which process you want to collect crashes from. Once initialized - * this way, the crashpad handler collects crashes from all processes. You still - * have to call `crashReporter.start` from the renderer or child process, otherwise - * crashes from them will get reported without `companyName`, `productName` or any - * of the `extra` information. + * **Note:** Parameters have limits on the length of the keys and values. Key names + * must be no longer than 39 bytes, and values must be no longer than 127 bytes. + * Keys with names longer than the maximum will be silently ignored. Key values + * longer than the maximum length will be truncated. */ - start(options: CrashReporterStartOptions): void; + addExtraParameter(key: string, value: string): void; }; export const process = (window as any).vscode.process as { diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 8b726400b97..e8ff8dda77a 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -82,6 +82,10 @@ import { WebviewMainService } from 'vs/platform/webview/electron-main/webviewMai import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; import { createServer, AddressInfo } from 'net'; import { IOpenExtensionWindowResult } from 'vs/platform/debug/common/extensionHostDebug'; +import { IFileService } from 'vs/platform/files/common/files'; +import { stripComments } from 'vs/base/common/json'; +import { generateUuid } from 'vs/base/common/uuid'; +import { VSBuffer } from 'vs/base/common/buffer'; export class CodeApplication extends Disposable { private windowsMainService: IWindowsMainService | undefined; @@ -134,11 +138,6 @@ export class CodeApplication extends Disposable { // // !!! DO NOT CHANGE without consulting the documentation !!! // - app.on('remote-get-guest-web-contents', event => { - this.logService.trace('App#on(remote-get-guest-web-contents): prevented'); - - event.preventDefault(); - }); app.on('remote-require', (event, sender, module) => { this.logService.trace('App#on(remote-require): prevented'); @@ -807,7 +806,7 @@ export class CodeApplication extends Disposable { return { fileUri: URI.file(path) }; } - private afterWindowOpen(accessor: ServicesAccessor): void { + private async afterWindowOpen(accessor: ServicesAccessor): Promise { // Signal phase: after window open this.lifecycleMainService.phase = LifecycleMainPhase.AfterWindowOpen; @@ -820,6 +819,34 @@ export class CodeApplication extends Disposable { if (updateService instanceof Win32UpdateService || updateService instanceof LinuxUpdateService || updateService instanceof DarwinUpdateService) { updateService.initialize(); } + + // If enable-crash-reporter argv is undefined then this is a fresh start, + // based on telemetry.enableCrashreporter settings, generate a UUID which + // will be used as crash reporter id and also update the json file. + try { + const fileService = accessor.get(IFileService); + const argvContent = await fileService.readFile(this.environmentService.argvResource); + const argvString = argvContent.value.toString(); + const argvJSON = JSON.parse(stripComments(argvString)); + if (argvJSON['enable-crash-reporter'] === undefined) { + const enableCrashReporter = this.configurationService.getValue('telemetry.enableCrashReporter') ?? true; + const additionalArgvContent = [ + '', + ' // Allows to disable crash reporting.', + ' // Should restart the app if the value is changed.', + ` "enable-crash-reporter": ${enableCrashReporter},`, + '', + ' // Unique id used for correlating crash reports sent from this instance.', + ' // Do not edit this value.', + ` "crash-reporter-id": "${generateUuid()}"`, + '}' + ]; + const newArgvString = argvString.substring(0, argvString.length - 2).concat(',\n', additionalArgvContent.join('\n')); + await fileService.writeFile(this.environmentService.argvResource, VSBuffer.fromString(newArgvString)); + } + } catch (error) { + this.logService.error(error); + } } private handleRemoteAuthorities(): void { diff --git a/src/vs/platform/electron/common/electron.ts b/src/vs/platform/electron/common/electron.ts index 1d65ceea6c6..759bcd96ab3 100644 --- a/src/vs/platform/electron/common/electron.ts +++ b/src/vs/platform/electron/common/electron.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { MessageBoxOptions, MessageBoxReturnValue, OpenDevToolsOptions, SaveDialogOptions, OpenDialogOptions, OpenDialogReturnValue, SaveDialogReturnValue, CrashReporterStartOptions, MouseInputEvent } from 'vs/base/parts/sandbox/common/electronTypes'; +import { MessageBoxOptions, MessageBoxReturnValue, OpenDevToolsOptions, SaveDialogOptions, OpenDialogOptions, OpenDialogReturnValue, SaveDialogReturnValue, MouseInputEvent } from 'vs/base/parts/sandbox/common/electronTypes'; import { IOpenedWindow, IWindowOpenable, IOpenEmptyWindowOptions, IOpenWindowOptions } from 'vs/platform/windows/common/windows'; import { INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; @@ -98,7 +98,6 @@ export interface ICommonElectronService { // Development openDevTools(options?: OpenDevToolsOptions): Promise; toggleDevTools(): Promise; - startCrashReporter(options: CrashReporterStartOptions): Promise; sendInputEvent(event: MouseInputEvent): Promise; // Connectivity diff --git a/src/vs/platform/electron/electron-main/electronMainService.ts b/src/vs/platform/electron/electron-main/electronMainService.ts index 0976846791f..0c2f9a735ca 100644 --- a/src/vs/platform/electron/electron-main/electronMainService.ts +++ b/src/vs/platform/electron/electron-main/electronMainService.ts @@ -5,7 +5,7 @@ import { Event } from 'vs/base/common/event'; import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; -import { MessageBoxOptions, MessageBoxReturnValue, shell, OpenDevToolsOptions, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue, CrashReporterStartOptions, crashReporter, Menu, BrowserWindow, app, clipboard, powerMonitor } from 'electron'; +import { MessageBoxOptions, MessageBoxReturnValue, shell, OpenDevToolsOptions, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue, Menu, BrowserWindow, app, clipboard, powerMonitor } from 'electron'; import { OpenContext } from 'vs/platform/windows/node/window'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { IOpenedWindow, IOpenWindowOptions, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; @@ -20,7 +20,6 @@ import { dirExists } from 'vs/base/node/pfs'; import { URI } from 'vs/base/common/uri'; import { ITelemetryData, ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { ILogService } from 'vs/platform/log/common/log'; import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { MouseInputEvent } from 'vs/base/parts/sandbox/common/electronTypes'; import { totalmem } from 'os'; @@ -38,8 +37,7 @@ export class ElectronMainService implements IElectronMainService { @IDialogMainService private readonly dialogMainService: IDialogMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IEnvironmentService private readonly environmentService: INativeEnvironmentService, - @ITelemetryService private readonly telemetryService: ITelemetryService, - @ILogService private readonly logService: ILogService + @ITelemetryService private readonly telemetryService: ITelemetryService ) { } @@ -479,12 +477,6 @@ export class ElectronMainService implements IElectronMainService { } } - async startCrashReporter(windowId: number | undefined, options: CrashReporterStartOptions): Promise { - this.logService.trace('ElectronMainService#crashReporter', JSON.stringify(options)); - - crashReporter.start(options); - } - async sendInputEvent(windowId: number | undefined, event: MouseInputEvent): Promise { const window = this.windowById(windowId); if (window && (event.type === 'mouseDown' || event.type === 'mouseUp')) { diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 2379b626c81..92dd2bcf87d 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -64,6 +64,7 @@ export interface ParsedArgs { 'disable-updates'?: boolean; 'disable-crash-reporter'?: boolean; 'crash-reporter-directory'?: string; + 'crash-reporter-id'?: string; 'skip-add-to-recently-opened'?: boolean; 'max-memory'?: string; 'file-write'?: boolean; @@ -182,6 +183,7 @@ export const OPTIONS: OptionDescriptions> = { 'disable-updates': { type: 'boolean' }, 'disable-crash-reporter': { type: 'boolean' }, 'crash-reporter-directory': { type: 'string' }, + 'crash-reporter-id': { type: 'string' }, 'disable-user-env-probe': { type: 'boolean' }, 'skip-add-to-recently-opened': { type: 'boolean' }, 'unity-launch': { type: 'boolean' }, diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index 5c0dc4ad4ae..45d5ec2cc02 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -256,7 +256,7 @@ export class EnvironmentService implements INativeEnvironmentService { get serviceMachineIdResource(): URI { return resources.joinPath(URI.file(this.userDataPath), 'machineid'); } get disableUpdates(): boolean { return !!this._args['disable-updates']; } - get disableCrashReporter(): boolean { return !!this._args['disable-crash-reporter']; } + get crashReporterId(): string | undefined { return this._args['crash-reporter-id']; } get crashReporterDirectory(): string | undefined { return this._args['crash-reporter-directory']; } get driverHandle(): string | undefined { return this._args['driver']; } diff --git a/src/vs/platform/launch/electron-main/launchMainService.ts b/src/vs/platform/launch/electron-main/launchMainService.ts index 233f1690c98..f1b54f87e9d 100644 --- a/src/vs/platform/launch/electron-main/launchMainService.ts +++ b/src/vs/platform/launch/electron-main/launchMainService.ts @@ -70,14 +70,18 @@ export class LaunchMainService implements ILaunchMainService { @IConfigurationService private readonly configurationService: IConfigurationService ) { } - start(args: ParsedArgs, userEnv: IProcessEnvironment): Promise { + async start(args: ParsedArgs, userEnv: IProcessEnvironment): Promise { this.logService.trace('Received data from other instance: ', args, userEnv); - const urlsToOpen = parseOpenUrl(args); + // Since we now start to open a window, make sure the app has focus. + // Focussing a window will not ensure that the application itself + // has focus, so we use the `steal: true` hint to force focus. + app.focus({ steal: true }); // Check early for open-url which is handled in URL service + const urlsToOpen = parseOpenUrl(args); if (urlsToOpen.length) { - let whenWindowReady: Promise = Promise.resolve(null); + let whenWindowReady: Promise = Promise.resolve(); // Create a window if there is none if (this.windowsMainService.getWindowCount() === 0) { @@ -91,12 +95,12 @@ export class LaunchMainService implements ILaunchMainService { this.urlService.open(url); } }); - - return Promise.resolve(undefined); } // Otherwise handle in windows service - return this.startOpenWindow(args, userEnv); + else { + return this.startOpenWindow(args, userEnv); + } } private startOpenWindow(args: ParsedArgs, userEnv: IProcessEnvironment): Promise { diff --git a/src/vs/platform/storage/node/storageIpc.ts b/src/vs/platform/storage/node/storageIpc.ts index 9db4e9afcda..8645255d9e7 100644 --- a/src/vs/platform/storage/node/storageIpc.ts +++ b/src/vs/platform/storage/node/storageIpc.ts @@ -10,7 +10,7 @@ import { IUpdateRequest, IStorageDatabase, IStorageItemsChangeEvent } from 'vs/b import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; import { generateUuid } from 'vs/base/common/uuid'; -import { instanceStorageKey, firstSessionDateStorageKey, lastSessionDateStorageKey, currentSessionDateStorageKey, crashReporterIdStorageKey } from 'vs/platform/telemetry/common/telemetry'; +import { instanceStorageKey, firstSessionDateStorageKey, lastSessionDateStorageKey, currentSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; type Key = string; type Value = string; @@ -49,16 +49,6 @@ export class GlobalStorageDatabaseChannel extends Disposable implements IServerC this.logService.error(`[storage] init(): Unable to init global storage due to ${error}`); } - // This is unique to the application instance and thereby - // should be written from the main process once. - // - // THIS SHOULD NEVER BE SENT TO TELEMETRY. - // - const crashReporterId = this.storageMainService.get(crashReporterIdStorageKey, undefined); - if (crashReporterId === undefined) { - this.storageMainService.store(crashReporterIdStorageKey, generateUuid()); - } - // Apply global telemetry values as part of the initialization // These are global across all windows and thereby should be // written from the main process once. diff --git a/src/vs/platform/telemetry/common/telemetry.ts b/src/vs/platform/telemetry/common/telemetry.ts index 523f81e8d0b..1acf9259110 100644 --- a/src/vs/platform/telemetry/common/telemetry.ts +++ b/src/vs/platform/telemetry/common/telemetry.ts @@ -57,4 +57,3 @@ export const currentSessionDateStorageKey = 'telemetry.currentSessionDate'; export const firstSessionDateStorageKey = 'telemetry.firstSessionDate'; export const lastSessionDateStorageKey = 'telemetry.lastSessionDate'; export const machineIdKey = 'telemetry.machineId'; -export const crashReporterIdStorageKey = 'crashReporter.guid'; diff --git a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts index 7886563a6ad..70e0e6a2137 100644 --- a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts +++ b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts @@ -23,7 +23,6 @@ import { IProductService } from 'vs/platform/product/common/productService'; interface IConfiguration extends IWindowsConfiguration { update: { mode: string; }; - telemetry: { enableCrashReporter: boolean }; debug: { console: { wordWrap: boolean } }; editor: { accessibilitySupport: 'on' | 'off' | 'auto' }; } @@ -35,7 +34,6 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo private nativeFullScreen: boolean | undefined; private clickThroughInactive: boolean | undefined; private updateMode: string | undefined; - private enableCrashReporter: boolean | undefined; private debugConsoleWordWrap: boolean | undefined; private accessibilitySupport: 'on' | 'off' | 'auto' | undefined; @@ -92,12 +90,6 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo changed = true; } - // Crash reporter - if (typeof config.telemetry?.enableCrashReporter === 'boolean' && config.telemetry.enableCrashReporter !== this.enableCrashReporter) { - this.enableCrashReporter = config.telemetry.enableCrashReporter; - changed = true; - } - // On linux turning on accessibility support will also pass this flag to the chrome renderer, thus a restart is required if (isLinux && typeof config.editor?.accessibilitySupport === 'string' && config.editor.accessibilitySupport !== this.accessibilitySupport) { this.accessibilitySupport = config.editor.accessibilitySupport; diff --git a/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.ts b/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.ts index 50398d48086..f21878522bb 100644 --- a/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.ts +++ b/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.ts @@ -17,6 +17,8 @@ import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common import { CancellationToken } from 'vs/base/common/cancellation'; import { IProductService } from 'vs/platform/product/common/productService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; export abstract class AbstractTelemetryOptOut implements IWorkbenchContribution { @@ -33,6 +35,8 @@ export abstract class AbstractTelemetryOptOut implements IWorkbenchContribution @IConfigurationService private readonly configurationService: IConfigurationService, @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, @IProductService private readonly productService: IProductService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IJSONEditingService private readonly jsonEditingService: IJSONEditingService ) { } protected async handleTelemetryOptOut(): Promise { @@ -136,10 +140,10 @@ export abstract class AbstractTelemetryOptOut implements IWorkbenchContribution }, { label: noLabel, - run: () => { + run: async () => { logTelemetry(true); this.configurationService.updateValue('telemetry.enableTelemetry', false); - this.configurationService.updateValue('telemetry.enableCrashReporter', false); + await this.jsonEditingService.write(this.environmentService.argvResource, [{ path: ['enable-crash-reporter'], value: false }], true); } } ], @@ -164,9 +168,11 @@ export class BrowserTelemetryOptOut extends AbstractTelemetryOptOut { @IExperimentService experimentService: IExperimentService, @IConfigurationService configurationService: IConfigurationService, @IExtensionGalleryService galleryService: IExtensionGalleryService, - @IProductService productService: IProductService + @IProductService productService: IProductService, + @IEnvironmentService environmentService: IEnvironmentService, + @IJSONEditingService jsonEditingService: IJSONEditingService ) { - super(storageService, openerService, notificationService, hostService, telemetryService, experimentService, configurationService, galleryService, productService); + super(storageService, openerService, notificationService, hostService, telemetryService, experimentService, configurationService, galleryService, productService, environmentService, jsonEditingService); this.handleTelemetryOptOut(); } diff --git a/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.ts b/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.ts index 0adc911b514..205e0e00d6a 100644 --- a/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.ts +++ b/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.ts @@ -13,6 +13,8 @@ import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common import { IProductService } from 'vs/platform/product/common/productService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { AbstractTelemetryOptOut } from 'vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; export class NativeTelemetryOptOut extends AbstractTelemetryOptOut { @@ -27,9 +29,11 @@ export class NativeTelemetryOptOut extends AbstractTelemetryOptOut { @IConfigurationService configurationService: IConfigurationService, @IExtensionGalleryService galleryService: IExtensionGalleryService, @IProductService productService: IProductService, + @IEnvironmentService environmentService: IEnvironmentService, + @IJSONEditingService jsonEditingService: IJSONEditingService, @IElectronService private readonly electronService: IElectronService ) { - super(storageService, openerService, notificationService, hostService, telemetryService, experimentService, configurationService, galleryService, productService); + super(storageService, openerService, notificationService, hostService, telemetryService, experimentService, configurationService, galleryService, productService, environmentService, jsonEditingService); this.handleTelemetryOptOut(); } diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 793b2394597..17ace9f6abe 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -6,13 +6,13 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import * as errors from 'vs/base/common/errors'; -import { equals, deepClone } from 'vs/base/common/objects'; +import { equals } from 'vs/base/common/objects'; import * as DOM from 'vs/base/browser/dom'; import { IAction, Separator } from 'vs/base/common/actions'; import { IFileService } from 'vs/platform/files/common/files'; import { toResource, IUntitledTextResourceEditorInput, SideBySideEditor, pathsToEditors } from 'vs/workbench/common/editor'; import { IEditorService, IResourceEditorInputType } from 'vs/workbench/services/editor/common/editorService'; -import { ITelemetryService, crashReporterIdStorageKey } from 'vs/platform/telemetry/common/telemetry'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWindowSettings, IOpenFileRequest, IWindowsConfiguration, getTitleBarStyle, IAddFoldersRequest } from 'vs/platform/windows/common/windows'; import { IRunActionInWindowRequest, IRunKeybindingInWindowRequest, INativeOpenFileRequest } from 'vs/platform/windows/node/window'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; @@ -22,8 +22,7 @@ import { setFullscreen, getZoomLevel } from 'vs/base/browser/browser'; import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/nativeKeymapService'; -import { CrashReporterStartOptions } from 'vs/base/parts/sandbox/common/electronTypes'; -import { crashReporter, ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; +import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { IMenuService, MenuId, IMenu, MenuItemAction, ICommandAction, SubmenuItemAction, MenuRegistry } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -33,8 +32,8 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { LifecyclePhase, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IWorkspaceFolderCreationData, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { IIntegrityService } from 'vs/workbench/services/integrity/common/integrity'; -import { isWindows, isMacintosh, isLinux } from 'vs/base/common/platform'; -import { IProductService, IAppCenterConfiguration } from 'vs/platform/product/common/productService'; +import { isWindows, isMacintosh } from 'vs/base/common/platform'; +import { IProductService } from 'vs/platform/product/common/productService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -46,7 +45,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { MenubarControl } from '../browser/parts/titlebar/menubarControl'; import { ILabelService } from 'vs/platform/label/common/label'; import { IUpdateService } from 'vs/platform/update/common/update'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService } from 'vs/platform/storage/common/storage'; import { IPreferencesService } from '../services/preferences/common/preferences'; import { IMenubarData, IMenubarMenu, IMenubarKeybinding, IMenubarMenuItemSubmenu, IMenubarMenuItemAction, MenubarMenuItem } from 'vs/platform/menubar/common/menubar'; import { IMenubarService } from 'vs/platform/menubar/electron-sandbox/menubar'; @@ -108,9 +107,8 @@ export class NativeWindow extends Disposable { @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService, @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService, - @IStorageService private readonly storageService: IStorageService, @IProductService private readonly productService: IProductService, - @IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService, + @IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService ) { super(); @@ -416,23 +414,6 @@ export class NativeWindow extends Disposable { // Touchbar menu (if enabled) this.updateTouchbarMenu(); - - // Crash reporter (if enabled) - if (!this.environmentService.disableCrashReporter && this.configurationService.getValue('telemetry.enableCrashReporter')) { - const companyName = this.productService.crashReporter?.companyName || 'Microsoft'; - const productName = this.productService.crashReporter?.productName || this.productService.nameShort; - - // With a provided crash reporter directory, crashes - // will be stored only locally in that folder - if (this.environmentService.crashReporterDirectory) { - this.setupCrashReporter(companyName, productName, undefined, this.environmentService.crashReporterDirectory); - } - - // With appCenter enabled, crashes will be uploaded - else if (this.productService.appCenter) { - this.setupCrashReporter(companyName, productName, this.productService.appCenter, undefined); - } - } } private setupOpenHandlers(): void { @@ -550,42 +531,6 @@ export class NativeWindow extends Disposable { } } - private async setupCrashReporter(companyName: string, productName: string, appCenter: IAppCenterConfiguration, crashesDirectory: undefined): Promise; - private async setupCrashReporter(companyName: string, productName: string, appCenter: undefined, crashesDirectory: string): Promise; - private async setupCrashReporter(companyName: string, productName: string, appCenter: IAppCenterConfiguration | undefined, crashesDirectory: string | undefined): Promise { - let submitURL: string | undefined = undefined; - if (appCenter) { - submitURL = isWindows ? appCenter[process.arch === 'ia32' ? 'win32-ia32' : 'win32-x64'] : isLinux ? appCenter[`linux-x64`] : appCenter.darwin; - } - - const info = await this.telemetryService.getTelemetryInfo(); - const crashReporterId = this.storageService.get(crashReporterIdStorageKey, StorageScope.GLOBAL)!; - - // base options with product info - const options: CrashReporterStartOptions = { - companyName, - productName, - submitURL: (submitURL?.concat('&uid=', crashReporterId, '&iid=', crashReporterId, '&sid=', info.sessionId)) || '', - extra: { - vscode_version: this.productService.version, - vscode_commit: this.productService.commit || '' - }, - - // If `crashesDirectory` is specified, we do not upload - uploadToServer: !crashesDirectory, - }; - - // start crash reporter in the main process first. - // On windows crashpad excepts a name pipe for the client to connect, - // this pipe is created by crash reporter initialization from the main process, - // changing this order of initialization will cause issues. - // For more info: https://chromium.googlesource.com/crashpad/crashpad/+/HEAD/doc/overview_design.md#normal-registration - await this.electronService.startCrashReporter(options); - - // start crash reporter right here - crashReporter.start(deepClone(options)); - } - private onAddFoldersRequest(request: IAddFoldersRequest): void { // Buffer all pending requests diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index 0296383f560..f608c5ff52d 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -344,6 +344,14 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; type: 'string', markdownDescription: nls.localize('argv.forceColorProfile', 'Allows to override the color profile to use. If you experience colors appear badly, try to set this to `srgb` and restart.') }, + 'enable-crash-reporter': { + type: 'boolean', + markdownDescription: nls.localize('argv.enableCrashReporter', 'Allows to disable crash reporting, should restart the app if the value is changed.') + }, + 'crash-reporter-id': { + type: 'string', + markdownDescription: nls.localize('argv.crashReporterId', 'Unique id used for correlating crash reports sent from this app instance.') + }, 'enable-proposed-api': { type: 'array', description: nls.localize('argv.enebleProposedApi', "Enable proposed APIs for a list of extension ids (such as \`vscode.git\`). Proposed APIs are unstable and subject to breaking without warning at any time. This should only be set for extension development and testing purposes."), diff --git a/src/vs/workbench/services/environment/electron-browser/environmentService.ts b/src/vs/workbench/services/environment/electron-browser/environmentService.ts index 20da095a459..46f084aff4c 100644 --- a/src/vs/workbench/services/environment/electron-browser/environmentService.ts +++ b/src/vs/workbench/services/environment/electron-browser/environmentService.ts @@ -17,8 +17,8 @@ export interface INativeWorkbenchEnvironmentService extends IWorkbenchEnvironmen readonly configuration: INativeEnvironmentConfiguration; - readonly disableCrashReporter: boolean; readonly crashReporterDirectory?: string; + readonly crashReporterId?: string; readonly cliPath: string; diff --git a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts index fdf086f74c7..7ec855ce59b 100644 --- a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts @@ -44,6 +44,7 @@ import { joinPath } from 'vs/base/common/resources'; import { Registry } from 'vs/platform/registry/common/platform'; import { IOutputChannelRegistry, Extensions } from 'vs/workbench/services/output/common/output'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; +import { isUUID } from 'vs/base/common/uuid'; export interface ILocalProcessExtensionHostInitData { readonly autoStart: boolean; @@ -182,18 +183,23 @@ export class LocalProcessExtensionHost implements IExtensionHost { opts.execArgv = ['--inspect-port=0']; } - // Enable the crash reporter depending on environment for local reporting - const crashesDirectory = this._environmentService.crashReporterDirectory; - if (crashesDirectory) { - const crashReporterOptions: CrashReporterStartOptions = { + // On linux crash reporter needs to be started on child node processes explicitly + if (platform.isLinux) { + const crashReporterStartOptions: CrashReporterStartOptions = { companyName: this._productService.crashReporter?.companyName || 'Microsoft', productName: this._productService.crashReporter?.productName || this._productService.nameShort, submitURL: '', - uploadToServer: false, - crashesDirectory + uploadToServer: false }; - - opts.env.CRASH_REPORTER_START_OPTIONS = JSON.stringify(crashReporterOptions); + const crashReporterId = this._environmentService.crashReporterId; // crashReporterId is set by the main process only when crash reporting is enabled by the user. + const appcenter = this._productService.appCenter; + const uploadCrashesToServer = !this._environmentService.crashReporterDirectory; // only upload unless --crash-reporter-directory is provided + if (uploadCrashesToServer && appcenter && crashReporterId && isUUID(crashReporterId)) { + const submitURL = appcenter[`linux-x64`]; + crashReporterStartOptions.submitURL = submitURL.concat('&uid=', crashReporterId, '&iid=', crashReporterId, '&sid=', crashReporterId); + crashReporterStartOptions.uploadToServer = true; + } + opts.env.CRASH_REPORTER_START_OPTIONS = JSON.stringify(crashReporterStartOptions); } // Run Extension Host as fork of current process diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index 9e555a89207..e2211240cd0 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -215,7 +215,6 @@ export class TestElectronService implements IElectronService { async exit(code: number): Promise { } async openDevTools(options?: Electron.OpenDevToolsOptions | undefined): Promise { } async toggleDevTools(): Promise { } - async startCrashReporter(options: Electron.CrashReporterStartOptions): Promise { } async resolveProxy(url: string): Promise { return undefined; } async readClipboardText(type?: 'selection' | 'clipboard' | undefined): Promise { return ''; } async writeClipboardText(text: string, type?: 'selection' | 'clipboard' | undefined): Promise { } diff --git a/test/unit/electron/index.js b/test/unit/electron/index.js index 4b5ce8cb9e9..440d8812b8f 100644 --- a/test/unit/electron/index.js +++ b/test/unit/electron/index.js @@ -12,6 +12,10 @@ const events = require('events'); const MochaJUnitReporter = require('mocha-junit-reporter'); const url = require('url'); +// Disable render process reuse, we still have +// non-context aware native modules in the renderer. +app.allowRendererProcessReuse = false; + const defaultReporterName = process.platform === 'win32' ? 'list' : 'spec'; const optimist = require('optimist') diff --git a/yarn.lock b/yarn.lock index 65f77846116..b760c375232 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2744,10 +2744,10 @@ electron-to-chromium@^1.2.7: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.27.tgz#78ecb8a399066187bb374eede35d9c70565a803d" integrity sha1-eOy4o5kGYYe7N07t412ccFZagD0= -electron@7.3.2: - version "7.3.2" - resolved "https://registry.yarnpkg.com/electron/-/electron-7.3.2.tgz#184b69fe9089693e179b3b34effa975dfc8e505d" - integrity sha512-5uSWVfCJogiPiU0G+RKi4ECnNs0gPNjAwYVE9KR7RXaOJYcpNIC5RFejaaUnuRoBssJ5B1n/5WU6wDUxvPajWQ== +electron@9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/electron/-/electron-9.2.0.tgz#d9fc8c8c9e5109669c366bd7b9ba83b06095d7a4" + integrity sha512-4ecZ3rcGg//Gk4fAK3Jo61T+uh36JhU6HHR/PTujQqQiBw1g4tNPd4R2hGGth2d+7FkRIs5GdRNef7h64fQEMw== dependencies: "@electron/get" "^1.0.1" "@types/node" "^12.0.12" From 1fc60f8b96ac30f8be61dd2f25af20b800de62dc Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Tue, 11 Aug 2020 15:12:16 -0400 Subject: [PATCH 126/736] Adds ExtensionContext.extensionRuntime - #104436 --- src/vs/vscode.proposed.d.ts | 19 +++++++++++++++++++ .../workbench/api/common/extHost.api.impl.ts | 1 + .../api/common/extHostExtensionService.ts | 7 ++++++- src/vs/workbench/api/common/extHostTypes.ts | 11 +++++++++++ .../api/node/extHostExtensionService.ts | 3 +++ .../api/worker/extHostExtensionService.ts | 2 ++ 6 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 2f8f0c0b82f..c261d8647b8 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -2005,4 +2005,23 @@ declare module 'vscode' { } //#endregion + + //#region https://github.com/microsoft/vscode/issues/104436 + + export enum ExtensionRuntime { + /** + * The extension is running in a NodeJS extension host. Runtime access to NodeJS APIs is available. + */ + Node = 1, + /** + * The extension is running in a Webworker extension host. Runtime access is limited to Webworker APIs. + */ + Webworker = 2 + } + + export interface ExtensionContext { + readonly extensionRuntime: ExtensionRuntime; + } + + //#endregion } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index da4884a3b76..667fa4c6fa9 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1036,6 +1036,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I EventEmitter: Emitter, ExtensionKind: extHostTypes.ExtensionKind, ExtensionMode: extHostTypes.ExtensionMode, + ExtensionRuntime: extHostTypes.ExtensionRuntime, CustomExecution: extHostTypes.CustomExecution, CustomExecution2: extHostTypes.CustomExecution, FileChangeType: extHostTypes.FileChangeType, diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 34639e18b6f..18600b6fff5 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -25,7 +25,7 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio import { Schemas } from 'vs/base/common/network'; import { VSBuffer } from 'vs/base/common/buffer'; import { ExtensionMemento } from 'vs/workbench/api/common/extHostMemento'; -import { RemoteAuthorityResolverError, ExtensionMode } from 'vs/workbench/api/common/extHostTypes'; +import { RemoteAuthorityResolverError, ExtensionMode, ExtensionRuntime } from 'vs/workbench/api/common/extHostTypes'; import { ResolvedAuthority, ResolvedOptions, RemoteAuthorityResolverErrorCode, IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; @@ -71,6 +71,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme readonly _serviceBrand: undefined; + abstract readonly extensionRuntime: ExtensionRuntime; private readonly _onDidChangeRemoteConnectionData = this._register(new Emitter()); public readonly onDidChangeRemoteConnectionData = this._onDidChangeRemoteConnectionData.event; @@ -408,6 +409,10 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme return that._storagePath.globalValue(extensionDescription); }, get extensionMode() { return extensionMode; }, + get extensionRuntime() { + checkProposedApiEnabled(extensionDescription); + return that.extensionRuntime; + }, get environmentVariableCollection() { return that._extHostTerminalService.getEnvironmentVariableCollection(extensionDescription); } }); }); diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 447fa8092c6..9dc828a248f 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2777,6 +2777,17 @@ export enum ExtensionMode { Test = 3, } +export enum ExtensionRuntime { + /** + * The extension is running in a NodeJS extension host. Runtime access to NodeJS APIs is available. + */ + Node = 1, + /** + * The extension is running in a Webworker extension host. Runtime access is limited to Webworker APIs. + */ + Webworker = 2 +} + //#endregion ExtensionContext export enum StandardTokenType { diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 8558835c744..3d92a699151 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -14,6 +14,7 @@ import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtensionRuntime } from 'vs/workbench/api/common/extHostTypes'; class NodeModuleRequireInterceptor extends RequireInterceptor { @@ -43,6 +44,8 @@ class NodeModuleRequireInterceptor extends RequireInterceptor { export class ExtHostExtensionService extends AbstractExtHostExtensionService { + readonly extensionRuntime = ExtensionRuntime.Node; + protected async _beforeAlmostReadyToRunExtensions(): Promise { // initialize API and register actors const extensionApiFactory = this._instaService.invokeFunction(createApiFactoryAndRegisterActors); diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts index c71ab1c7da4..870c5df9ad9 100644 --- a/src/vs/workbench/api/worker/extHostExtensionService.ts +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -9,6 +9,7 @@ import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHost import { URI } from 'vs/base/common/uri'; import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterceptor'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtensionRuntime } from 'vs/workbench/api/common/extHostTypes'; class WorkerRequireInterceptor extends RequireInterceptor { @@ -31,6 +32,7 @@ class WorkerRequireInterceptor extends RequireInterceptor { } export class ExtHostExtensionService extends AbstractExtHostExtensionService { + readonly extensionRuntime = ExtensionRuntime.Node; private _fakeModules?: WorkerRequireInterceptor; From 846dde8de0c9e89da1eade2c8323b7cee13233d5 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 11 Aug 2020 14:16:58 -0700 Subject: [PATCH 127/736] add more tests. --- src/vs/editor/common/model/textModel.ts | 14 ++++- .../contrib/find/test/findModel.test.ts | 52 +++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 224e4b16637..cb7b34b3451 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -1137,6 +1137,18 @@ export class TextModel extends Disposable implements model.ITextModel { searchRanges = [this.getFullModelRange()]; } + searchRanges = searchRanges.sort((d1, d2) => d1.startLineNumber - d2.startLineNumber || d1.startColumn - d2.startColumn); + + const uniqueSearchRanges: Range[] = []; + uniqueSearchRanges.push(searchRanges.reduce((prev, curr) => { + if (Range.areIntersecting(prev, curr)) { + return prev.plusRange(curr); + } + + uniqueSearchRanges.push(prev); + return curr; + })); + let matchMapper: (value: Range, index: number, array: Range[]) => model.FindMatch[]; if (!isRegex && searchString.indexOf('\n') < 0) { // not regex, not multi line @@ -1152,7 +1164,7 @@ export class TextModel extends Disposable implements model.ITextModel { matchMapper = (searchRange: Range) => TextModelSearch.findMatches(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchRange, captureMatches, limitResultCount); } - return searchRanges.map(matchMapper).reduce((arr, matches: model.FindMatch[]) => arr.concat(matches), []); + return uniqueSearchRanges.map(matchMapper).reduce((arr, matches: model.FindMatch[]) => arr.concat(matches), []); } public findNextMatch(searchString: string, rawSearchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string, captureMatches: boolean): model.FindMatch | null { diff --git a/src/vs/editor/contrib/find/test/findModel.test.ts b/src/vs/editor/contrib/find/test/findModel.test.ts index 218c52192a8..70c5494985a 100644 --- a/src/vs/editor/contrib/find/test/findModel.test.ts +++ b/src/vs/editor/contrib/find/test/findModel.test.ts @@ -493,6 +493,58 @@ suite('FindModel', () => { findState.dispose(); }); + findTest('multi-selection find model next stays in scope (overlap)', (editor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello', wholeWord: true, searchScope: [new Range(7, 1, 8, 2), new Range(8, 1, 9, 1)] }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [7, 14, 7, 19], + [7, 14, 7, 19], + [ + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [8, 14, 8, 19], + [8, 14, 8, 19], + [ + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [7, 14, 7, 19], + [7, 14, 7, 19], + [ + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.dispose(); + findState.dispose(); + }); + findTest('multi-selection find model next stays in scope', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'hello', matchCase: true, wholeWord: false, searchScope: [new Range(6, 1, 7, 38), new Range(9, 3, 9, 38)] }, false); From b84660fa86af95b072853f1d6686cff8f4976c12 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 11 Aug 2020 15:23:56 -0700 Subject: [PATCH 128/736] Add more specific checks for being on TS 4.0.1 Fixes #104456 --- .../typescript-language-features/src/tsServer/spawner.ts | 3 +-- extensions/typescript-language-features/src/utils/api.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/extensions/typescript-language-features/src/tsServer/spawner.ts b/extensions/typescript-language-features/src/tsServer/spawner.ts index 51a4096f9b3..70f1b41574e 100644 --- a/extensions/typescript-language-features/src/tsServer/spawner.ts +++ b/extensions/typescript-language-features/src/tsServer/spawner.ts @@ -18,7 +18,6 @@ import { ILogDirectoryProvider } from './logDirectoryProvider'; import { GetErrRoutingTsServer, ITypeScriptServer, ProcessBasedTsServer, SyntaxRoutingTsServer, TsServerDelegate, TsServerProcessFactory, TsServerProcessKind } from './server'; import { TypeScriptVersionManager } from './versionManager'; import { ITypeScriptVersionProvider, TypeScriptVersion } from './versionProvider'; -import * as semver from 'semver'; const enum CompositeServerType { /** Run a single server that handles all commands */ @@ -164,7 +163,7 @@ export class TypeScriptServerSpawner { let tsServerLogFile: string | undefined; if (kind === TsServerProcessKind.Syntax) { - if (semver.gte(API.v401rc.fullVersionString, apiVersion.fullVersionString)) { + if (apiVersion.gte(API.v401)) { args.push('--serverMode', 'partialSemantic'); } else { args.push('--syntaxOnly'); diff --git a/extensions/typescript-language-features/src/utils/api.ts b/extensions/typescript-language-features/src/utils/api.ts index ea9c2493e8a..289c091f5b1 100644 --- a/extensions/typescript-language-features/src/utils/api.ts +++ b/extensions/typescript-language-features/src/utils/api.ts @@ -34,8 +34,8 @@ export default class API { public static readonly v380 = API.fromSimpleString('3.8.0'); public static readonly v381 = API.fromSimpleString('3.8.1'); public static readonly v390 = API.fromSimpleString('3.9.0'); - public static readonly v401rc = API.fromSimpleString('4.0.1-rc'); public static readonly v400 = API.fromSimpleString('4.0.0'); + public static readonly v401 = API.fromSimpleString('4.0.1'); public static fromVersionString(versionString: string): API { let version = semver.valid(versionString); From e54b6f5220e80ce3130e2c0f4995a3233c0754c3 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 11 Aug 2020 16:28:19 -0700 Subject: [PATCH 129/736] Update marked to 1.1.0 This copies of marked. We also disable a console.warn about using santize since we already use `insane` --- src/vs/base/common/marked/marked.js | 3891 ++++++++++------- .../test/browser/markdownRenderer.test.ts | 2 - 2 files changed, 2402 insertions(+), 1491 deletions(-) diff --git a/src/vs/base/common/marked/marked.js b/src/vs/base/common/marked/marked.js index 1288f459647..fb133b90fb8 100644 --- a/src/vs/base/common/marked/marked.js +++ b/src/vs/base/common/marked/marked.js @@ -1,1714 +1,2627 @@ /** * marked - a markdown parser - * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed) + * Copyright (c) 2011-2020, Christopher Jeffrey. (MIT Licensed) * https://github.com/markedjs/marked */ -// BEGIN MONACOCHANGE -var __marked_exports; -// END MONACOCHANGE - -;(function(root) { -'use strict'; - /** - * Block-Level Grammar + * DO NOT EDIT THIS FILE + * The code in this file is generated from files in ./src/ */ -var block = { - newline: /^\n+/, - code: /^( {4}[^\n]+\n*)+/, - fences: noop, - hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/, - heading: /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/, - nptable: noop, - blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/, - list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/, - html: '^ {0,3}(?:' // optional indentation - + '<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)' // (1) - + '|comment[^\\n]*(\\n+|$)' // (2) - + '|<\\?[\\s\\S]*?\\?>\\n*' // (3) - + '|\\n*' // (4) - + '|\\n*' // (5) - + '|)[\\s\\S]*?(?:\\n{2,}|$)' // (6) - + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag - + '|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag - + ')', - def: /^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/, - table: noop, - lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/, - paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading| {0,3}>|<\/?(?:tag)(?: +|\n|\/?>)|<(?:script|pre|style|!--))[^\n]+)*)/, - text: /^[^\n]+/ -}; +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.marked = factory()); +}(this, (function () { 'use strict'; -block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/; -block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/; -block.def = edit(block.def) - .replace('label', block._label) - .replace('title', block._title) - .getRegex(); - -block.bullet = /(?:[*+-]|\d{1,9}\.)/; -block.item = /^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/; -block.item = edit(block.item, 'gm') - .replace(/bull/g, block.bullet) - .getRegex(); - -block.list = edit(block.list) - .replace(/bull/g, block.bullet) - .replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))') - .replace('def', '\\n+(?=' + block.def.source + ')') - .getRegex(); - -block._tag = 'address|article|aside|base|basefont|blockquote|body|caption' - + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption' - + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe' - + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option' - + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr' - + '|track|ul'; -block._comment = //; -block.html = edit(block.html, 'i') - .replace('comment', block._comment) - .replace('tag', block._tag) - .replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/) - .getRegex(); - -block.paragraph = edit(block.paragraph) - .replace('hr', block.hr) - .replace('heading', block.heading) - .replace('lheading', block.lheading) - .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks - .getRegex(); - -block.blockquote = edit(block.blockquote) - .replace('paragraph', block.paragraph) - .getRegex(); - -/** - * Normal Block Grammar - */ - -block.normal = merge({}, block); - -/** - * GFM Block Grammar - */ - -block.gfm = merge({}, block.normal, { - fences: /^ {0,3}(`{3,}|~{3,})([^`\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/, - paragraph: /^/, - heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/ -}); - -block.gfm.paragraph = edit(block.paragraph) - .replace('(?!', '(?!' - + block.gfm.fences.source.replace('\\1', '\\2') + '|' - + block.list.source.replace('\\1', '\\3') + '|') - .getRegex(); - -/** - * GFM + Tables Block Grammar - */ - -block.tables = merge({}, block.gfm, { - nptable: /^ *([^|\n ].*\|.*)\n *([-:]+ *\|[-| :]*)(?:\n((?:.*[^>\n ].*(?:\n|$))*)\n*|$)/, - table: /^ *\|(.+)\n *\|?( *[-:]+[-| :]*)(?:\n((?: *[^>\n ].*(?:\n|$))*)\n*|$)/ -}); - -/** - * Pedantic grammar - */ - -block.pedantic = merge({}, block.normal, { - html: edit( - '^ *(?:comment *(?:\\n|\\s*$)' - + '|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)' // closed tag - + '|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))') - .replace('comment', block._comment) - .replace(/tag/g, '(?!(?:' - + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub' - + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)' - + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b') - .getRegex(), - def: /^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/ -}); - -/** - * Block Lexer - */ - -function Lexer(options) { - this.tokens = []; - this.tokens.links = Object.create(null); - this.options = options || marked.defaults; - this.rules = block.normal; - - if (this.options.pedantic) { - this.rules = block.pedantic; - } else if (this.options.gfm) { - if (this.options.tables) { - this.rules = block.tables; - } else { - this.rules = block.gfm; + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); } } -} -/** - * Expose Block Rules - */ + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; + } -Lexer.rules = block; + function _unsupportedIterableToArray(o, minLen) { + if (!o) return; + if (typeof o === "string") return _arrayLikeToArray(o, minLen); + var n = Object.prototype.toString.call(o).slice(8, -1); + if (n === "Object" && o.constructor) n = o.constructor.name; + if (n === "Map" || n === "Set") return Array.from(o); + if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); + } -/** - * Static Lex Method - */ + function _arrayLikeToArray(arr, len) { + if (len == null || len > arr.length) len = arr.length; -Lexer.lex = function(src, options) { - var lexer = new Lexer(options); - return lexer.lex(src); -}; + for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; -/** - * Preprocessing - */ + return arr2; + } -Lexer.prototype.lex = function(src) { - src = src - .replace(/\r\n|\r/g, '\n') - .replace(/\t/g, ' ') - .replace(/\u00a0/g, ' ') - .replace(/\u2424/g, '\n'); + function _createForOfIteratorHelperLoose(o, allowArrayLike) { + var it; - return this.token(src, true); -}; + if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { + if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { + if (it) o = it; + var i = 0; + return function () { + if (i >= o.length) return { + done: true + }; + return { + done: false, + value: o[i++] + }; + }; + } -/** - * Lexing - */ + throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); + } -Lexer.prototype.token = function(src, top) { - src = src.replace(/^ +$/gm, ''); - var next, - loose, - cap, - bull, - b, - item, - listStart, - listItems, - t, - space, - i, - tag, - l, - isordered, - istask, - ischecked; + it = o[Symbol.iterator](); + return it.next.bind(it); + } - while (src) { - // newline - if (cap = this.rules.newline.exec(src)) { - src = src.substring(cap[0].length); - if (cap[0].length > 1) { - this.tokens.push({ - type: 'space' - }); + function createCommonjsModule(fn, module) { + return module = { exports: {} }, fn(module, module.exports), module.exports; + } + + var defaults = createCommonjsModule(function (module) { + function getDefaults() { + return { + baseUrl: null, + breaks: false, + gfm: true, + headerIds: true, + headerPrefix: '', + highlight: null, + langPrefix: 'language-', + mangle: true, + pedantic: false, + renderer: null, + sanitize: false, + sanitizer: null, + silent: false, + smartLists: false, + smartypants: false, + tokenizer: null, + walkTokens: null, + xhtml: false + }; + } + + function changeDefaults(newDefaults) { + module.exports.defaults = newDefaults; + } + + module.exports = { + defaults: getDefaults(), + getDefaults: getDefaults, + changeDefaults: changeDefaults + }; + }); + var defaults_1 = defaults.defaults; + var defaults_2 = defaults.getDefaults; + var defaults_3 = defaults.changeDefaults; + + /** + * Helpers + */ + var escapeTest = /[&<>"']/; + var escapeReplace = /[&<>"']/g; + var escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/; + var escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g; + var escapeReplacements = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + + var getEscapeReplacement = function getEscapeReplacement(ch) { + return escapeReplacements[ch]; + }; + + function escape(html, encode) { + if (encode) { + if (escapeTest.test(html)) { + return html.replace(escapeReplace, getEscapeReplacement); + } + } else { + if (escapeTestNoEncode.test(html)) { + return html.replace(escapeReplaceNoEncode, getEscapeReplacement); } } - // code - if (cap = this.rules.code.exec(src)) { - src = src.substring(cap[0].length); - cap = cap[0].replace(/^ {4}/gm, ''); - this.tokens.push({ - type: 'code', - text: !this.options.pedantic - ? rtrim(cap, '\n') - : cap - }); - continue; - } + return html; + } - // fences (gfm) - if (cap = this.rules.fences.exec(src)) { - src = src.substring(cap[0].length); - this.tokens.push({ - type: 'code', - lang: cap[2] ? cap[2].trim() : cap[2], - text: cap[3] || '' - }); - continue; - } + var unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig; - // heading - if (cap = this.rules.heading.exec(src)) { - src = src.substring(cap[0].length); - this.tokens.push({ - type: 'heading', - depth: cap[1].length, - text: cap[2] - }); - continue; - } + function unescape(html) { + // explicitly match decimal, hex, and named HTML entities + return html.replace(unescapeTest, function (_, n) { + n = n.toLowerCase(); + if (n === 'colon') return ':'; - // table no leading pipe (gfm) - if (cap = this.rules.nptable.exec(src)) { - item = { - type: 'table', - header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), - align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), - cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [] - }; + if (n.charAt(0) === '#') { + return n.charAt(1) === 'x' ? String.fromCharCode(parseInt(n.substring(2), 16)) : String.fromCharCode(+n.substring(1)); + } - if (item.header.length === item.align.length) { - src = src.substring(cap[0].length); + return ''; + }); + } - for (i = 0; i < item.align.length; i++) { - if (/^ *-+: *$/.test(item.align[i])) { - item.align[i] = 'right'; - } else if (/^ *:-+: *$/.test(item.align[i])) { - item.align[i] = 'center'; - } else if (/^ *:-+ *$/.test(item.align[i])) { - item.align[i] = 'left'; - } else { - item.align[i] = null; - } - } + var caret = /(^|[^\[])\^/g; - for (i = 0; i < item.cells.length; i++) { - item.cells[i] = splitCells(item.cells[i], item.header.length); - } + function edit(regex, opt) { + regex = regex.source || regex; + opt = opt || ''; + var obj = { + replace: function replace(name, val) { + val = val.source || val; + val = val.replace(caret, '$1'); + regex = regex.replace(name, val); + return obj; + }, + getRegex: function getRegex() { + return new RegExp(regex, opt); + } + }; + return obj; + } - this.tokens.push(item); + var nonWordAndColonTest = /[^\w:]/g; + var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i; - continue; + function cleanUrl(sanitize, base, href) { + if (sanitize) { + var prot; + + try { + prot = decodeURIComponent(unescape(href)).replace(nonWordAndColonTest, '').toLowerCase(); + } catch (e) { + return null; + } + + if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) { + return null; } } - // hr - if (cap = this.rules.hr.exec(src)) { - src = src.substring(cap[0].length); - this.tokens.push({ - type: 'hr' - }); - continue; + if (base && !originIndependentUrl.test(href)) { + href = resolveUrl(base, href); } - // blockquote - if (cap = this.rules.blockquote.exec(src)) { - src = src.substring(cap[0].length); - - this.tokens.push({ - type: 'blockquote_start' - }); - - cap = cap[0].replace(/^ *> ?/gm, ''); - - // Pass `top` to keep the current - // "toplevel" state. This is exactly - // how markdown.pl works. - this.token(cap, top); - - this.tokens.push({ - type: 'blockquote_end' - }); - - continue; + try { + href = encodeURI(href).replace(/%25/g, '%'); + } catch (e) { + return null; } - // list - if (cap = this.rules.list.exec(src)) { - src = src.substring(cap[0].length); - bull = cap[2]; - isordered = bull.length > 1; + return href; + } - listStart = { - type: 'list_start', - ordered: isordered, - start: isordered ? +bull : '', - loose: false + var baseUrls = {}; + var justDomain = /^[^:]+:\/*[^/]*$/; + var protocol = /^([^:]+:)[\s\S]*$/; + var domain = /^([^:]+:\/*[^/]*)[\s\S]*$/; + + function resolveUrl(base, href) { + if (!baseUrls[' ' + base]) { + // we can ignore everything in base after the last slash of its path component, + // but we might need to add _that_ + // https://tools.ietf.org/html/rfc3986#section-3 + if (justDomain.test(base)) { + baseUrls[' ' + base] = base + '/'; + } else { + baseUrls[' ' + base] = rtrim(base, '/', true); + } + } + + base = baseUrls[' ' + base]; + var relativeBase = base.indexOf(':') === -1; + + if (href.substring(0, 2) === '//') { + if (relativeBase) { + return href; + } + + return base.replace(protocol, '$1') + href; + } else if (href.charAt(0) === '/') { + if (relativeBase) { + return href; + } + + return base.replace(domain, '$1') + href; + } else { + return base + href; + } + } + + var noopTest = { + exec: function noopTest() {} + }; + + function merge(obj) { + var i = 1, + target, + key; + + for (; i < arguments.length; i++) { + target = arguments[i]; + + for (key in target) { + if (Object.prototype.hasOwnProperty.call(target, key)) { + obj[key] = target[key]; + } + } + } + + return obj; + } + + function splitCells(tableRow, count) { + // ensure that every cell-delimiting pipe has a space + // before it to distinguish it from an escaped pipe + var row = tableRow.replace(/\|/g, function (match, offset, str) { + var escaped = false, + curr = offset; + + while (--curr >= 0 && str[curr] === '\\') { + escaped = !escaped; + } + + if (escaped) { + // odd number of slashes means | is escaped + // so we leave it alone + return '|'; + } else { + // add space before unescaped | + return ' |'; + } + }), + cells = row.split(/ \|/); + var i = 0; + + if (cells.length > count) { + cells.splice(count); + } else { + while (cells.length < count) { + cells.push(''); + } + } + + for (; i < cells.length; i++) { + // leading or trailing whitespace is ignored per the gfm spec + cells[i] = cells[i].trim().replace(/\\\|/g, '|'); + } + + return cells; + } // Remove trailing 'c's. Equivalent to str.replace(/c*$/, ''). + // /c*$/ is vulnerable to REDOS. + // invert: Remove suffix of non-c chars instead. Default falsey. + + + function rtrim(str, c, invert) { + var l = str.length; + + if (l === 0) { + return ''; + } // Length of suffix matching the invert condition. + + + var suffLen = 0; // Step left until we fail to match the invert condition. + + while (suffLen < l) { + var currChar = str.charAt(l - suffLen - 1); + + if (currChar === c && !invert) { + suffLen++; + } else if (currChar !== c && invert) { + suffLen++; + } else { + break; + } + } + + return str.substr(0, l - suffLen); + } + + function findClosingBracket(str, b) { + if (str.indexOf(b[1]) === -1) { + return -1; + } + + var l = str.length; + var level = 0, + i = 0; + + for (; i < l; i++) { + if (str[i] === '\\') { + i++; + } else if (str[i] === b[0]) { + level++; + } else if (str[i] === b[1]) { + level--; + + if (level < 0) { + return i; + } + } + } + + return -1; + } + + function checkSanitizeDeprecation(opt) { + if (opt && opt.sanitize && !opt.silent) { + // VS CODE CHANGE + // Disable logging about sanitize options. We already use insane after running the sanitizer + + // console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options'); + } + } + + var helpers = { + escape: escape, + unescape: unescape, + edit: edit, + cleanUrl: cleanUrl, + resolveUrl: resolveUrl, + noopTest: noopTest, + merge: merge, + splitCells: splitCells, + rtrim: rtrim, + findClosingBracket: findClosingBracket, + checkSanitizeDeprecation: checkSanitizeDeprecation + }; + + var defaults$1 = defaults.defaults; + var rtrim$1 = helpers.rtrim, + splitCells$1 = helpers.splitCells, + _escape = helpers.escape, + findClosingBracket$1 = helpers.findClosingBracket; + + function outputLink(cap, link, raw) { + var href = link.href; + var title = link.title ? _escape(link.title) : null; + var text = cap[1].replace(/\\([\[\]])/g, '$1'); + + if (cap[0].charAt(0) !== '!') { + return { + type: 'link', + raw: raw, + href: href, + title: title, + text: text }; + } else { + return { + type: 'image', + raw: raw, + href: href, + title: title, + text: _escape(text) + }; + } + } - this.tokens.push(listStart); + function indentCodeCompensation(raw, text) { + var matchIndentToCode = raw.match(/^(\s+)(?:```)/); - // Get each top-level item. - cap = cap[0].match(this.rules.item); + if (matchIndentToCode === null) { + return text; + } - listItems = []; - next = false; - l = cap.length; - i = 0; + var indentToCode = matchIndentToCode[1]; + return text.split('\n').map(function (node) { + var matchIndentInNode = node.match(/^\s+/); - for (; i < l; i++) { - item = cap[i]; + if (matchIndentInNode === null) { + return node; + } - // Remove the list item's bullet - // so it is seen as the next token. - space = item.length; - item = item.replace(/^ *([*+-]|\d+\.) */, ''); + var indentInNode = matchIndentInNode[0]; - // Outdent whatever the - // list item contains. Hacky. - if (~item.indexOf('\n ')) { - space -= item.length; - item = !this.options.pedantic - ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') - : item.replace(/^ {1,4}/gm, ''); + if (indentInNode.length >= indentToCode.length) { + return node.slice(indentToCode.length); + } + + return node; + }).join('\n'); + } + /** + * Tokenizer + */ + + + var Tokenizer_1 = /*#__PURE__*/function () { + function Tokenizer(options) { + this.options = options || defaults$1; + } + + var _proto = Tokenizer.prototype; + + _proto.space = function space(src) { + var cap = this.rules.block.newline.exec(src); + + if (cap) { + if (cap[0].length > 1) { + return { + type: 'space', + raw: cap[0] + }; } - // Determine whether the next list item belongs here. - // Backpedal if it does not belong in this list. - if (i !== l - 1) { - b = block.bullet.exec(cap[i + 1])[0]; - if (bull.length > 1 ? b.length === 1 - : (b.length > 1 || (this.options.smartLists && b !== bull))) { - src = cap.slice(i + 1).join('\n') + src; - i = l - 1; - } + return { + raw: '\n' + }; + } + }; + + _proto.code = function code(src, tokens) { + var cap = this.rules.block.code.exec(src); + + if (cap) { + var lastToken = tokens[tokens.length - 1]; // An indented code block cannot interrupt a paragraph. + + if (lastToken && lastToken.type === 'paragraph') { + return { + raw: cap[0], + text: cap[0].trimRight() + }; } - // Determine whether item is loose or not. - // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ - // for discount behavior. - loose = next || /\n\n(?!\s*$)/.test(item); - if (i !== l - 1) { - next = item.charAt(item.length - 1) === '\n'; - if (!loose) loose = next; - } + var text = cap[0].replace(/^ {4}/gm, ''); + return { + type: 'code', + raw: cap[0], + codeBlockStyle: 'indented', + text: !this.options.pedantic ? rtrim$1(text, '\n') : text + }; + } + }; - if (loose) { - listStart.loose = true; - } + _proto.fences = function fences(src) { + var cap = this.rules.block.fences.exec(src); - // Check for task list items - istask = /^\[[ xX]\] /.test(item); - ischecked = undefined; - if (istask) { - ischecked = item[1] !== ' '; - item = item.replace(/^\[[ xX]\] +/, ''); - } + if (cap) { + var raw = cap[0]; + var text = indentCodeCompensation(raw, cap[3] || ''); + return { + type: 'code', + raw: raw, + lang: cap[2] ? cap[2].trim() : cap[2], + text: text + }; + } + }; - t = { - type: 'list_item_start', - task: istask, - checked: ischecked, - loose: loose + _proto.heading = function heading(src) { + var cap = this.rules.block.heading.exec(src); + + if (cap) { + return { + type: 'heading', + raw: cap[0], + depth: cap[1].length, + text: cap[2] + }; + } + }; + + _proto.nptable = function nptable(src) { + var cap = this.rules.block.nptable.exec(src); + + if (cap) { + var item = { + type: 'table', + header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')), + align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), + cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [], + raw: cap[0] }; - listItems.push(t); - this.tokens.push(t); + if (item.header.length === item.align.length) { + var l = item.align.length; + var i; - // Recurse. - this.token(item, false); + for (i = 0; i < l; i++) { + if (/^ *-+: *$/.test(item.align[i])) { + item.align[i] = 'right'; + } else if (/^ *:-+: *$/.test(item.align[i])) { + item.align[i] = 'center'; + } else if (/^ *:-+ *$/.test(item.align[i])) { + item.align[i] = 'left'; + } else { + item.align[i] = null; + } + } - this.tokens.push({ - type: 'list_item_end' - }); - } + l = item.cells.length; - if (listStart.loose) { - l = listItems.length; - i = 0; - for (; i < l; i++) { - listItems[i].loose = true; + for (i = 0; i < l; i++) { + item.cells[i] = splitCells$1(item.cells[i], item.header.length); + } + + return item; } } + }; - this.tokens.push({ - type: 'list_end' - }); + _proto.hr = function hr(src) { + var cap = this.rules.block.hr.exec(src); - continue; - } + if (cap) { + return { + type: 'hr', + raw: cap[0] + }; + } + }; - // html - if (cap = this.rules.html.exec(src)) { - src = src.substring(cap[0].length); - this.tokens.push({ - type: this.options.sanitize - ? 'paragraph' - : 'html', - pre: !this.options.sanitizer - && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'), - text: cap[0] - }); - continue; - } + _proto.blockquote = function blockquote(src) { + var cap = this.rules.block.blockquote.exec(src); - // def - if (top && (cap = this.rules.def.exec(src))) { - src = src.substring(cap[0].length); - if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1); - tag = cap[1].toLowerCase().replace(/\s+/g, ' '); - if (!this.tokens.links[tag]) { - this.tokens.links[tag] = { + if (cap) { + var text = cap[0].replace(/^ *> ?/gm, ''); + return { + type: 'blockquote', + raw: cap[0], + text: text + }; + } + }; + + _proto.list = function list(src) { + var cap = this.rules.block.list.exec(src); + + if (cap) { + var raw = cap[0]; + var bull = cap[2]; + var isordered = bull.length > 1; + var isparen = bull[bull.length - 1] === ')'; + var list = { + type: 'list', + raw: raw, + ordered: isordered, + start: isordered ? +bull.slice(0, -1) : '', + loose: false, + items: [] + }; // Get each top-level item. + + var itemMatch = cap[0].match(this.rules.block.item); + var next = false, + item, + space, + b, + addBack, + loose, + istask, + ischecked; + var l = itemMatch.length; + + for (var i = 0; i < l; i++) { + item = itemMatch[i]; + raw = item; // Remove the list item's bullet + // so it is seen as the next token. + + space = item.length; + item = item.replace(/^ *([*+-]|\d+[.)]) */, ''); // Outdent whatever the + // list item contains. Hacky. + + if (~item.indexOf('\n ')) { + space -= item.length; + item = !this.options.pedantic ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') : item.replace(/^ {1,4}/gm, ''); + } // Determine whether the next list item belongs here. + // Backpedal if it does not belong in this list. + + + if (i !== l - 1) { + b = this.rules.block.bullet.exec(itemMatch[i + 1])[0]; + + if (isordered ? b.length === 1 || !isparen && b[b.length - 1] === ')' : b.length > 1 || this.options.smartLists && b !== bull) { + addBack = itemMatch.slice(i + 1).join('\n'); + list.raw = list.raw.substring(0, list.raw.length - addBack.length); + i = l - 1; + } + } // Determine whether item is loose or not. + // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ + // for discount behavior. + + + loose = next || /\n\n(?!\s*$)/.test(item); + + if (i !== l - 1) { + next = item.charAt(item.length - 1) === '\n'; + if (!loose) loose = next; + } + + if (loose) { + list.loose = true; + } // Check for task list items + + + istask = /^\[[ xX]\] /.test(item); + ischecked = undefined; + + if (istask) { + ischecked = item[1] !== ' '; + item = item.replace(/^\[[ xX]\] +/, ''); + } + + list.items.push({ + type: 'list_item', + raw: raw, + task: istask, + checked: ischecked, + loose: loose, + text: item + }); + } + + return list; + } + }; + + _proto.html = function html(src) { + var cap = this.rules.block.html.exec(src); + + if (cap) { + return { + type: this.options.sanitize ? 'paragraph' : 'html', + raw: cap[0], + pre: !this.options.sanitizer && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'), + text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0] + }; + } + }; + + _proto.def = function def(src) { + var cap = this.rules.block.def.exec(src); + + if (cap) { + if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1); + var tag = cap[1].toLowerCase().replace(/\s+/g, ' '); + return { + tag: tag, + raw: cap[0], href: cap[2], title: cap[3] }; } - continue; - } + }; - // table (gfm) - if (cap = this.rules.table.exec(src)) { - item = { - type: 'table', - header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), - align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), - cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [] - }; + _proto.table = function table(src) { + var cap = this.rules.block.table.exec(src); - if (item.header.length === item.align.length) { - src = src.substring(cap[0].length); + if (cap) { + var item = { + type: 'table', + header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')), + align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), + cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [] + }; - for (i = 0; i < item.align.length; i++) { - if (/^ *-+: *$/.test(item.align[i])) { - item.align[i] = 'right'; - } else if (/^ *:-+: *$/.test(item.align[i])) { - item.align[i] = 'center'; - } else if (/^ *:-+ *$/.test(item.align[i])) { - item.align[i] = 'left'; + if (item.header.length === item.align.length) { + item.raw = cap[0]; + var l = item.align.length; + var i; + + for (i = 0; i < l; i++) { + if (/^ *-+: *$/.test(item.align[i])) { + item.align[i] = 'right'; + } else if (/^ *:-+: *$/.test(item.align[i])) { + item.align[i] = 'center'; + } else if (/^ *:-+ *$/.test(item.align[i])) { + item.align[i] = 'left'; + } else { + item.align[i] = null; + } + } + + l = item.cells.length; + + for (i = 0; i < l; i++) { + item.cells[i] = splitCells$1(item.cells[i].replace(/^ *\| *| *\| *$/g, ''), item.header.length); + } + + return item; + } + } + }; + + _proto.lheading = function lheading(src) { + var cap = this.rules.block.lheading.exec(src); + + if (cap) { + return { + type: 'heading', + raw: cap[0], + depth: cap[2].charAt(0) === '=' ? 1 : 2, + text: cap[1] + }; + } + }; + + _proto.paragraph = function paragraph(src) { + var cap = this.rules.block.paragraph.exec(src); + + if (cap) { + return { + type: 'paragraph', + raw: cap[0], + text: cap[1].charAt(cap[1].length - 1) === '\n' ? cap[1].slice(0, -1) : cap[1] + }; + } + }; + + _proto.text = function text(src, tokens) { + var cap = this.rules.block.text.exec(src); + + if (cap) { + var lastToken = tokens[tokens.length - 1]; + + if (lastToken && lastToken.type === 'text') { + return { + raw: cap[0], + text: cap[0] + }; + } + + return { + type: 'text', + raw: cap[0], + text: cap[0] + }; + } + }; + + _proto.escape = function escape(src) { + var cap = this.rules.inline.escape.exec(src); + + if (cap) { + return { + type: 'escape', + raw: cap[0], + text: _escape(cap[1]) + }; + } + }; + + _proto.tag = function tag(src, inLink, inRawBlock) { + var cap = this.rules.inline.tag.exec(src); + + if (cap) { + if (!inLink && /^/i.test(cap[0])) { + inLink = false; + } + + if (!inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { + inRawBlock = true; + } else if (inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { + inRawBlock = false; + } + + return { + type: this.options.sanitize ? 'text' : 'html', + raw: cap[0], + inLink: inLink, + inRawBlock: inRawBlock, + text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0] + }; + } + }; + + _proto.link = function link(src) { + var cap = this.rules.inline.link.exec(src); + + if (cap) { + var lastParenIndex = findClosingBracket$1(cap[2], '()'); + + if (lastParenIndex > -1) { + var start = cap[0].indexOf('!') === 0 ? 5 : 4; + var linkLen = start + cap[1].length + lastParenIndex; + cap[2] = cap[2].substring(0, lastParenIndex); + cap[0] = cap[0].substring(0, linkLen).trim(); + cap[3] = ''; + } + + var href = cap[2]; + var title = ''; + + if (this.options.pedantic) { + var link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href); + + if (link) { + href = link[1]; + title = link[3]; } else { - item.align[i] = null; + title = ''; + } + } else { + title = cap[3] ? cap[3].slice(1, -1) : ''; + } + + href = href.trim().replace(/^<([\s\S]*)>$/, '$1'); + var token = outputLink(cap, { + href: href ? href.replace(this.rules.inline._escapes, '$1') : href, + title: title ? title.replace(this.rules.inline._escapes, '$1') : title + }, cap[0]); + return token; + } + }; + + _proto.reflink = function reflink(src, links) { + var cap; + + if ((cap = this.rules.inline.reflink.exec(src)) || (cap = this.rules.inline.nolink.exec(src))) { + var link = (cap[2] || cap[1]).replace(/\s+/g, ' '); + link = links[link.toLowerCase()]; + + if (!link || !link.href) { + var text = cap[0].charAt(0); + return { + type: 'text', + raw: text, + text: text + }; + } + + var token = outputLink(cap, link, cap[0]); + return token; + } + }; + + _proto.strong = function strong(src, maskedSrc, prevChar) { + if (prevChar === void 0) { + prevChar = ''; + } + + var match = this.rules.inline.strong.start.exec(src); + + if (match && (!match[1] || match[1] && (prevChar === '' || this.rules.inline.punctuation.exec(prevChar)))) { + maskedSrc = maskedSrc.slice(-1 * src.length); + var endReg = match[0] === '**' ? this.rules.inline.strong.endAst : this.rules.inline.strong.endUnd; + endReg.lastIndex = 0; + var cap; + + while ((match = endReg.exec(maskedSrc)) != null) { + cap = this.rules.inline.strong.middle.exec(maskedSrc.slice(0, match.index + 3)); + + if (cap) { + return { + type: 'strong', + raw: src.slice(0, cap[0].length), + text: src.slice(2, cap[0].length - 2) + }; + } + } + } + }; + + _proto.em = function em(src, maskedSrc, prevChar) { + if (prevChar === void 0) { + prevChar = ''; + } + + var match = this.rules.inline.em.start.exec(src); + + if (match && (!match[1] || match[1] && (prevChar === '' || this.rules.inline.punctuation.exec(prevChar)))) { + maskedSrc = maskedSrc.slice(-1 * src.length); + var endReg = match[0] === '*' ? this.rules.inline.em.endAst : this.rules.inline.em.endUnd; + endReg.lastIndex = 0; + var cap; + + while ((match = endReg.exec(maskedSrc)) != null) { + cap = this.rules.inline.em.middle.exec(maskedSrc.slice(0, match.index + 2)); + + if (cap) { + return { + type: 'em', + raw: src.slice(0, cap[0].length), + text: src.slice(1, cap[0].length - 1) + }; + } + } + } + }; + + _proto.codespan = function codespan(src) { + var cap = this.rules.inline.code.exec(src); + + if (cap) { + var text = cap[2].replace(/\n/g, ' '); + var hasNonSpaceChars = /[^ ]/.test(text); + var hasSpaceCharsOnBothEnds = text.startsWith(' ') && text.endsWith(' '); + + if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) { + text = text.substring(1, text.length - 1); + } + + text = _escape(text, true); + return { + type: 'codespan', + raw: cap[0], + text: text + }; + } + }; + + _proto.br = function br(src) { + var cap = this.rules.inline.br.exec(src); + + if (cap) { + return { + type: 'br', + raw: cap[0] + }; + } + }; + + _proto.del = function del(src) { + var cap = this.rules.inline.del.exec(src); + + if (cap) { + return { + type: 'del', + raw: cap[0], + text: cap[1] + }; + } + }; + + _proto.autolink = function autolink(src, mangle) { + var cap = this.rules.inline.autolink.exec(src); + + if (cap) { + var text, href; + + if (cap[2] === '@') { + text = _escape(this.options.mangle ? mangle(cap[1]) : cap[1]); + href = 'mailto:' + text; + } else { + text = _escape(cap[1]); + href = text; + } + + return { + type: 'link', + raw: cap[0], + text: text, + href: href, + tokens: [{ + type: 'text', + raw: text, + text: text + }] + }; + } + }; + + _proto.url = function url(src, mangle) { + var cap; + + if (cap = this.rules.inline.url.exec(src)) { + var text, href; + + if (cap[2] === '@') { + text = _escape(this.options.mangle ? mangle(cap[0]) : cap[0]); + href = 'mailto:' + text; + } else { + // do extended autolink path validation + var prevCapZero; + + do { + prevCapZero = cap[0]; + cap[0] = this.rules.inline._backpedal.exec(cap[0])[0]; + } while (prevCapZero !== cap[0]); + + text = _escape(cap[0]); + + if (cap[1] === 'www.') { + href = 'http://' + text; + } else { + href = text; } } - for (i = 0; i < item.cells.length; i++) { - item.cells[i] = splitCells( - item.cells[i].replace(/^ *\| *| *\| *$/g, ''), - item.header.length); + return { + type: 'link', + raw: cap[0], + text: text, + href: href, + tokens: [{ + type: 'text', + raw: text, + text: text + }] + }; + } + }; + + _proto.inlineText = function inlineText(src, inRawBlock, smartypants) { + var cap = this.rules.inline.text.exec(src); + + if (cap) { + var text; + + if (inRawBlock) { + text = this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0]; + } else { + text = _escape(this.options.smartypants ? smartypants(cap[0]) : cap[0]); } - this.tokens.push(item); - - continue; + return { + type: 'text', + raw: cap[0], + text: text + }; } - } + }; - // lheading - if (cap = this.rules.lheading.exec(src)) { - src = src.substring(cap[0].length); - this.tokens.push({ - type: 'heading', - depth: cap[2] === '=' ? 1 : 2, - text: cap[1] - }); - continue; - } + return Tokenizer; + }(); - // top-level paragraph - if (top && (cap = this.rules.paragraph.exec(src))) { - src = src.substring(cap[0].length); - this.tokens.push({ - type: 'paragraph', - text: cap[1].charAt(cap[1].length - 1) === '\n' - ? cap[1].slice(0, -1) - : cap[1] - }); - continue; - } + var noopTest$1 = helpers.noopTest, + edit$1 = helpers.edit, + merge$1 = helpers.merge; + /** + * Block-Level Grammar + */ - // text - if (cap = this.rules.text.exec(src)) { - // Top-level should never reach here. - src = src.substring(cap[0].length); - this.tokens.push({ - type: 'text', - text: cap[0] - }); - continue; - } + var block = { + newline: /^\n+/, + code: /^( {4}[^\n]+\n*)+/, + fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/, + hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/, + heading: /^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/, + blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/, + list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/, + html: '^ {0,3}(?:' // optional indentation + + '<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)' // (1) + + '|comment[^\\n]*(\\n+|$)' // (2) + + '|<\\?[\\s\\S]*?(?:\\?>\\n*|$)' // (3) + + '|\\n*|$)' // (4) + + '|\\n*|$)' // (5) + + '|)[\\s\\S]*?(?:\\n{2,}|$)' // (6) + + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag + + '|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag + + ')', + def: /^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/, + nptable: noopTest$1, + table: noopTest$1, + lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/, + // regex template, placeholders will be replaced according to different paragraph + // interruption rules of commonmark and the original markdown spec: + _paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html)[^\n]+)*)/, + text: /^[^\n]+/ + }; + block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/; + block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/; + block.def = edit$1(block.def).replace('label', block._label).replace('title', block._title).getRegex(); + block.bullet = /(?:[*+-]|\d{1,9}[.)])/; + block.item = /^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/; + block.item = edit$1(block.item, 'gm').replace(/bull/g, block.bullet).getRegex(); + block.list = edit$1(block.list).replace(/bull/g, block.bullet).replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))').replace('def', '\\n+(?=' + block.def.source + ')').getRegex(); + block._tag = 'address|article|aside|base|basefont|blockquote|body|caption' + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption' + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe' + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option' + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr' + '|track|ul'; + block._comment = /|$)/; + block.html = edit$1(block.html, 'i').replace('comment', block._comment).replace('tag', block._tag).replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(); + block.paragraph = edit$1(block._paragraph).replace('hr', block.hr).replace('heading', ' {0,3}#{1,6} ').replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs + .replace('blockquote', ' {0,3}>').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt + .replace('html', ')|<(?:script|pre|style|!--)').replace('tag', block._tag) // pars can be interrupted by type (6) html blocks + .getRegex(); + block.blockquote = edit$1(block.blockquote).replace('paragraph', block.paragraph).getRegex(); + /** + * Normal Block Grammar + */ - if (src) { - throw new Error('Infinite loop on byte: ' + src.charCodeAt(0)); - } - } + block.normal = merge$1({}, block); + /** + * GFM Block Grammar + */ - return this.tokens; -}; + block.gfm = merge$1({}, block.normal, { + nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header + + ' *([-:]+ *\\|[-| :]*)' // Align + + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)', + // Cells + table: '^ *\\|(.+)\\n' // Header + + ' *\\|?( *[-:]+[-| :]*)' // Align + + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells -/** - * Inline-Level Grammar - */ + }); + block.gfm.nptable = edit$1(block.gfm.nptable).replace('hr', block.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt + .replace('html', ')|<(?:script|pre|style|!--)').replace('tag', block._tag) // tables can be interrupted by type (6) html blocks + .getRegex(); + block.gfm.table = edit$1(block.gfm.table).replace('hr', block.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt + .replace('html', ')|<(?:script|pre|style|!--)').replace('tag', block._tag) // tables can be interrupted by type (6) html blocks + .getRegex(); + /** + * Pedantic grammar (original John Gruber's loose markdown specification) + */ -var inline = { - escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/, - autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/, - url: noop, - tag: '^comment' - + '|^' // self-closing tag + block.pedantic = merge$1({}, block.normal, { + html: edit$1('^ *(?:comment *(?:\\n|\\s*$)' + '|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)' // closed tag + + '|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))').replace('comment', block._comment).replace(/tag/g, '(?!(?:' + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub' + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)' + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b').getRegex(), + def: /^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/, + heading: /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/, + fences: noopTest$1, + // fences not supported + paragraph: edit$1(block.normal._paragraph).replace('hr', block.hr).replace('heading', ' *#{1,6} *[^\n]').replace('lheading', block.lheading).replace('blockquote', ' {0,3}>').replace('|fences', '').replace('|list', '').replace('|html', '').getRegex() + }); + /** + * Inline-Level Grammar + */ + + var inline = { + escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/, + autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/, + url: noopTest$1, + tag: '^comment' + '|^' // self-closing tag + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. + '|^' // declaration, e.g. - + '|^', // CDATA section - link: /^!?\[(label)\]\(href(?:\s+(title))?\s*\)/, - reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/, - nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/, - strong: /^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/, - em: /^_([^\s_])_(?!_)|^\*([^\s*"<\[])\*(?!\*)|^_([^\s][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s"<\[][\s\S]*?[^\s*])\*(?!\*)|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/, - code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/, - br: /^( {2,}|\\)\n(?!\s*$)/, - del: noop, - text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\', + // CDATA section + link: /^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/, + reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/, + nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/, + reflinkSearch: 'reflink|nolink(?!\\()', + strong: { + start: /^(?:(\*\*(?=[*punctuation]))|\*\*)(?![\s])|__/, + // (1) returns if starts w/ punctuation + middle: /^\*\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*\*$|^__(?![\s])((?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?)__$/, + endAst: /[^punctuation\s]\*\*(?!\*)|[punctuation]\*\*(?!\*)(?:(?=[punctuation\s]|$))/, + // last char can't be punct, or final * must also be followed by punct (or endline) + endUnd: /[^\s]__(?!_)(?:(?=[punctuation\s])|$)/ // last char can't be a space, and final _ must preceed punct or \s (or endline) -// list of punctuation marks from common mark spec -// without ` and ] to workaround Rule 17 (inline code blocks/links) -inline._punctuation = '!"#$%&\'()*+,\\-./:;<=>?@\\[^_{|}~'; -inline.em = edit(inline.em).replace(/punctuation/g, inline._punctuation).getRegex(); + }, + em: { + start: /^(?:(\*(?=[punctuation]))|\*)(?![*\s])|_/, + // (1) returns if starts w/ punctuation + middle: /^\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*$|^_(?![_\s])(?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?_$/, + endAst: /[^punctuation\s]\*(?!\*)|[punctuation]\*(?!\*)(?:(?=[punctuation\s]|$))/, + // last char can't be punct, or final * must also be followed by punct (or endline) + endUnd: /[^\s]_(?!_)(?:(?=[punctuation\s])|$)/ // last char can't be a space, and final _ must preceed punct or \s (or endline) -inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g; + }, + code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/, + br: /^( {2,}|\\)\n(?!\s*$)/, + del: noopTest$1, + text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\?@\\[\\]`^{|}~'; + inline.punctuation = edit$1(inline.punctuation).replace(/punctuation/g, inline._punctuation).getRegex(); // sequences em should skip over [title](link), `code`, -inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/; + inline._blockSkip = '\\[[^\\]]*?\\]\\([^\\)]*?\\)|`[^`]*?`|<[^>]*?>'; + inline._overlapSkip = '__[^_]*?__|\\*\\*\\[^\\*\\]*?\\*\\*'; + inline._comment = edit$1(block._comment).replace('(?:-->|$)', '-->').getRegex(); + inline.em.start = edit$1(inline.em.start).replace(/punctuation/g, inline._punctuation).getRegex(); + inline.em.middle = edit$1(inline.em.middle).replace(/punctuation/g, inline._punctuation).replace(/overlapSkip/g, inline._overlapSkip).getRegex(); + inline.em.endAst = edit$1(inline.em.endAst, 'g').replace(/punctuation/g, inline._punctuation).getRegex(); + inline.em.endUnd = edit$1(inline.em.endUnd, 'g').replace(/punctuation/g, inline._punctuation).getRegex(); + inline.strong.start = edit$1(inline.strong.start).replace(/punctuation/g, inline._punctuation).getRegex(); + inline.strong.middle = edit$1(inline.strong.middle).replace(/punctuation/g, inline._punctuation).replace(/blockSkip/g, inline._blockSkip).getRegex(); + inline.strong.endAst = edit$1(inline.strong.endAst, 'g').replace(/punctuation/g, inline._punctuation).getRegex(); + inline.strong.endUnd = edit$1(inline.strong.endUnd, 'g').replace(/punctuation/g, inline._punctuation).getRegex(); + inline.blockSkip = edit$1(inline._blockSkip, 'g').getRegex(); + inline.overlapSkip = edit$1(inline._overlapSkip, 'g').getRegex(); + inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g; + inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/; + inline._email = /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/; + inline.autolink = edit$1(inline.autolink).replace('scheme', inline._scheme).replace('email', inline._email).getRegex(); + inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/; + inline.tag = edit$1(inline.tag).replace('comment', inline._comment).replace('attribute', inline._attribute).getRegex(); + inline._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/; + inline._href = /<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/; + inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/; + inline.link = edit$1(inline.link).replace('label', inline._label).replace('href', inline._href).replace('title', inline._title).getRegex(); + inline.reflink = edit$1(inline.reflink).replace('label', inline._label).getRegex(); + inline.reflinkSearch = edit$1(inline.reflinkSearch, 'g').replace('reflink', inline.reflink).replace('nolink', inline.nolink).getRegex(); + /** + * Normal Inline Grammar + */ -inline.tag = edit(inline.tag) - .replace('comment', block._comment) - .replace('attribute', inline._attribute) - .getRegex(); + inline.normal = merge$1({}, inline); + /** + * Pedantic Inline Grammar + */ -inline._label = /(?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|`(?!`)|[^\[\]\\`])*?/; -inline._href = /\s*(<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*)/; -inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/; + inline.pedantic = merge$1({}, inline.normal, { + strong: { + start: /^__|\*\*/, + middle: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, + endAst: /\*\*(?!\*)/g, + endUnd: /__(?!_)/g + }, + em: { + start: /^_|\*/, + middle: /^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/, + endAst: /\*(?!\*)/g, + endUnd: /_(?!_)/g + }, + link: edit$1(/^!?\[(label)\]\((.*?)\)/).replace('label', inline._label).getRegex(), + reflink: edit$1(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace('label', inline._label).getRegex() + }); + /** + * GFM Inline Grammar + */ -inline.link = edit(inline.link) - .replace('label', inline._label) - .replace('href', inline._href) - .replace('title', inline._title) - .getRegex(); + inline.gfm = merge$1({}, inline.normal, { + escape: edit$1(inline.escape).replace('])', '~|])').getRegex(), + _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/, + url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/, + _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/, + del: /^~+(?=\S)([\s\S]*?\S)~+/, + text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\ 0.5) { + ch = 'x' + ch.toString(16); + } + + out += '&#' + ch + ';'; } + + return out; } -} + /** + * Block Lexer + */ -/** - * Expose Inline Rules - */ -InlineLexer.rules = inline; + var Lexer_1 = /*#__PURE__*/function () { + function Lexer(options) { + this.tokens = []; + this.tokens.links = Object.create(null); + this.options = options || defaults$2; + this.options.tokenizer = this.options.tokenizer || new Tokenizer_1(); + this.tokenizer = this.options.tokenizer; + this.tokenizer.options = this.options; + var rules = { + block: block$1.normal, + inline: inline$1.normal + }; -/** - * Static Lexing/Compiling Method - */ - -InlineLexer.output = function(src, links, options) { - var inline = new InlineLexer(links, options); - return inline.output(src); -}; - -/** - * Lexing/Compiling - */ - -InlineLexer.prototype.output = function(src) { - var out = '', - link, - text, - href, - title, - cap, - prevCapZero; - - while (src) { - // escape - if (cap = this.rules.escape.exec(src)) { - src = src.substring(cap[0].length); - out += escape(cap[1]); - continue; - } - - // tag - if (cap = this.rules.tag.exec(src)) { - if (!this.inLink && /^/i.test(cap[0])) { - this.inLink = false; - } - if (!this.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { - this.inRawBlock = true; - } else if (this.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { - this.inRawBlock = false; - } - - src = src.substring(cap[0].length); - out += this.options.sanitize - ? this.options.sanitizer - ? this.options.sanitizer(cap[0]) - : escape(cap[0]) - : cap[0]; - continue; - } - - // link - if (cap = this.rules.link.exec(src)) { - var lastParenIndex = findClosingBracket(cap[2], '()'); - if (lastParenIndex > -1) { - var linkLen = cap[0].length - (cap[2].length - lastParenIndex) - (cap[3] || '').length; - cap[2] = cap[2].substring(0, lastParenIndex); - cap[0] = cap[0].substring(0, linkLen).trim(); - cap[3] = ''; - } - src = src.substring(cap[0].length); - this.inLink = true; - href = cap[2]; if (this.options.pedantic) { - link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href); + rules.block = block$1.pedantic; + rules.inline = inline$1.pedantic; + } else if (this.options.gfm) { + rules.block = block$1.gfm; - if (link) { - href = link[1]; - title = link[3]; + if (this.options.breaks) { + rules.inline = inline$1.breaks; } else { - title = ''; - } - } else { - title = cap[3] ? cap[3].slice(1, -1) : ''; - } - href = href.trim().replace(/^<([\s\S]*)>$/, '$1'); - out += this.outputLink(cap, { - href: InlineLexer.escapes(href), - title: InlineLexer.escapes(title) - }); - this.inLink = false; - continue; - } - - // reflink, nolink - if ((cap = this.rules.reflink.exec(src)) - || (cap = this.rules.nolink.exec(src))) { - src = src.substring(cap[0].length); - link = (cap[2] || cap[1]).replace(/\s+/g, ' '); - link = this.links[link.toLowerCase()]; - if (!link || !link.href) { - out += cap[0].charAt(0); - src = cap[0].substring(1) + src; - continue; - } - this.inLink = true; - out += this.outputLink(cap, link); - this.inLink = false; - continue; - } - - // strong - if (cap = this.rules.strong.exec(src)) { - src = src.substring(cap[0].length); - out += this.renderer.strong(this.output(cap[4] || cap[3] || cap[2] || cap[1])); - continue; - } - - // em - if (cap = this.rules.em.exec(src)) { - src = src.substring(cap[0].length); - out += this.renderer.em(this.output(cap[6] || cap[5] || cap[4] || cap[3] || cap[2] || cap[1])); - continue; - } - - // code - if (cap = this.rules.code.exec(src)) { - src = src.substring(cap[0].length); - out += this.renderer.codespan(escape(cap[2].trim(), true)); - continue; - } - - // br - if (cap = this.rules.br.exec(src)) { - src = src.substring(cap[0].length); - out += this.renderer.br(); - continue; - } - - // del (gfm) - if (cap = this.rules.del.exec(src)) { - src = src.substring(cap[0].length); - out += this.renderer.del(this.output(cap[1])); - continue; - } - - // autolink - if (cap = this.rules.autolink.exec(src)) { - src = src.substring(cap[0].length); - if (cap[2] === '@') { - text = escape(this.mangle(cap[1])); - href = 'mailto:' + text; - } else { - text = escape(cap[1]); - href = text; - } - out += this.renderer.link(href, null, text); - continue; - } - - // url (gfm) - if (!this.inLink && (cap = this.rules.url.exec(src))) { - if (cap[2] === '@') { - text = escape(cap[0]); - href = 'mailto:' + text; - } else { - // do extended autolink path validation - do { - prevCapZero = cap[0]; - cap[0] = this.rules._backpedal.exec(cap[0])[0]; - } while (prevCapZero !== cap[0]); - text = escape(cap[0]); - if (cap[1] === 'www.') { - href = 'http://' + text; - } else { - href = text; + rules.inline = inline$1.gfm; } } - src = src.substring(cap[0].length); - out += this.renderer.link(href, null, text); - continue; - } - // text - if (cap = this.rules.text.exec(src)) { - src = src.substring(cap[0].length); - if (this.inRawBlock) { - out += this.renderer.text(cap[0]); - } else { - out += this.renderer.text(escape(this.smartypants(cap[0]))); + this.tokenizer.rules = rules; + } + /** + * Expose Rules + */ + + + /** + * Static Lex Method + */ + Lexer.lex = function lex(src, options) { + var lexer = new Lexer(options); + return lexer.lex(src); + } + /** + * Preprocessing + */ + ; + + var _proto = Lexer.prototype; + + _proto.lex = function lex(src) { + src = src.replace(/\r\n|\r/g, '\n').replace(/\t/g, ' '); + this.blockTokens(src, this.tokens, true); + this.inline(this.tokens); + return this.tokens; + } + /** + * Lexing + */ + ; + + _proto.blockTokens = function blockTokens(src, tokens, top) { + if (tokens === void 0) { + tokens = []; } - continue; + + if (top === void 0) { + top = true; + } + + src = src.replace(/^ +$/gm, ''); + var token, i, l, lastToken; + + while (src) { + // newline + if (token = this.tokenizer.space(src)) { + src = src.substring(token.raw.length); + + if (token.type) { + tokens.push(token); + } + + continue; + } // code + + + if (token = this.tokenizer.code(src, tokens)) { + src = src.substring(token.raw.length); + + if (token.type) { + tokens.push(token); + } else { + lastToken = tokens[tokens.length - 1]; + lastToken.raw += '\n' + token.raw; + lastToken.text += '\n' + token.text; + } + + continue; + } // fences + + + if (token = this.tokenizer.fences(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // heading + + + if (token = this.tokenizer.heading(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // table no leading pipe (gfm) + + + if (token = this.tokenizer.nptable(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // hr + + + if (token = this.tokenizer.hr(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // blockquote + + + if (token = this.tokenizer.blockquote(src)) { + src = src.substring(token.raw.length); + token.tokens = this.blockTokens(token.text, [], top); + tokens.push(token); + continue; + } // list + + + if (token = this.tokenizer.list(src)) { + src = src.substring(token.raw.length); + l = token.items.length; + + for (i = 0; i < l; i++) { + token.items[i].tokens = this.blockTokens(token.items[i].text, [], false); + } + + tokens.push(token); + continue; + } // html + + + if (token = this.tokenizer.html(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // def + + + if (top && (token = this.tokenizer.def(src))) { + src = src.substring(token.raw.length); + + if (!this.tokens.links[token.tag]) { + this.tokens.links[token.tag] = { + href: token.href, + title: token.title + }; + } + + continue; + } // table (gfm) + + + if (token = this.tokenizer.table(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // lheading + + + if (token = this.tokenizer.lheading(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // top-level paragraph + + + if (top && (token = this.tokenizer.paragraph(src))) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // text + + + if (token = this.tokenizer.text(src, tokens)) { + src = src.substring(token.raw.length); + + if (token.type) { + tokens.push(token); + } else { + lastToken = tokens[tokens.length - 1]; + lastToken.raw += '\n' + token.raw; + lastToken.text += '\n' + token.text; + } + + continue; + } + + if (src) { + var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0); + + if (this.options.silent) { + console.error(errMsg); + break; + } else { + throw new Error(errMsg); + } + } + } + + return tokens; + }; + + _proto.inline = function inline(tokens) { + var i, j, k, l2, row, token; + var l = tokens.length; + + for (i = 0; i < l; i++) { + token = tokens[i]; + + switch (token.type) { + case 'paragraph': + case 'text': + case 'heading': + { + token.tokens = []; + this.inlineTokens(token.text, token.tokens); + break; + } + + case 'table': + { + token.tokens = { + header: [], + cells: [] + }; // header + + l2 = token.header.length; + + for (j = 0; j < l2; j++) { + token.tokens.header[j] = []; + this.inlineTokens(token.header[j], token.tokens.header[j]); + } // cells + + + l2 = token.cells.length; + + for (j = 0; j < l2; j++) { + row = token.cells[j]; + token.tokens.cells[j] = []; + + for (k = 0; k < row.length; k++) { + token.tokens.cells[j][k] = []; + this.inlineTokens(row[k], token.tokens.cells[j][k]); + } + } + + break; + } + + case 'blockquote': + { + this.inline(token.tokens); + break; + } + + case 'list': + { + l2 = token.items.length; + + for (j = 0; j < l2; j++) { + this.inline(token.items[j].tokens); + } + + break; + } + } + } + + return tokens; + } + /** + * Lexing/Compiling + */ + ; + + _proto.inlineTokens = function inlineTokens(src, tokens, inLink, inRawBlock, prevChar) { + if (tokens === void 0) { + tokens = []; + } + + if (inLink === void 0) { + inLink = false; + } + + if (inRawBlock === void 0) { + inRawBlock = false; + } + + if (prevChar === void 0) { + prevChar = ''; + } + + var token; // String with links masked to avoid interference with em and strong + + var maskedSrc = src; + var match; // Mask out reflinks + + if (this.tokens.links) { + var links = Object.keys(this.tokens.links); + + if (links.length > 0) { + while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) { + if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) { + maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex); + } + } + } + } // Mask out other blocks + + + while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) { + maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); + } + + while (src) { + // escape + if (token = this.tokenizer.escape(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // tag + + + if (token = this.tokenizer.tag(src, inLink, inRawBlock)) { + src = src.substring(token.raw.length); + inLink = token.inLink; + inRawBlock = token.inRawBlock; + tokens.push(token); + continue; + } // link + + + if (token = this.tokenizer.link(src)) { + src = src.substring(token.raw.length); + + if (token.type === 'link') { + token.tokens = this.inlineTokens(token.text, [], true, inRawBlock); + } + + tokens.push(token); + continue; + } // reflink, nolink + + + if (token = this.tokenizer.reflink(src, this.tokens.links)) { + src = src.substring(token.raw.length); + + if (token.type === 'link') { + token.tokens = this.inlineTokens(token.text, [], true, inRawBlock); + } + + tokens.push(token); + continue; + } // strong + + + if (token = this.tokenizer.strong(src, maskedSrc, prevChar)) { + src = src.substring(token.raw.length); + token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock); + tokens.push(token); + continue; + } // em + + + if (token = this.tokenizer.em(src, maskedSrc, prevChar)) { + src = src.substring(token.raw.length); + token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock); + tokens.push(token); + continue; + } // code + + + if (token = this.tokenizer.codespan(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // br + + + if (token = this.tokenizer.br(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // del (gfm) + + + if (token = this.tokenizer.del(src)) { + src = src.substring(token.raw.length); + token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock); + tokens.push(token); + continue; + } // autolink + + + if (token = this.tokenizer.autolink(src, mangle)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // url (gfm) + + + if (!inLink && (token = this.tokenizer.url(src, mangle))) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // text + + + if (token = this.tokenizer.inlineText(src, inRawBlock, smartypants)) { + src = src.substring(token.raw.length); + prevChar = token.raw.slice(-1); + tokens.push(token); + continue; + } + + if (src) { + var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0); + + if (this.options.silent) { + console.error(errMsg); + break; + } else { + throw new Error(errMsg); + } + } + } + + return tokens; + }; + + _createClass(Lexer, null, [{ + key: "rules", + get: function get() { + return { + block: block$1, + inline: inline$1 + }; + } + }]); + + return Lexer; + }(); + + var defaults$3 = defaults.defaults; + var cleanUrl$1 = helpers.cleanUrl, + escape$1 = helpers.escape; + /** + * Renderer + */ + + var Renderer_1 = /*#__PURE__*/function () { + function Renderer(options) { + this.options = options || defaults$3; } - if (src) { - throw new Error('Infinite loop on byte: ' + src.charCodeAt(0)); - } - } + var _proto = Renderer.prototype; - return out; -}; + _proto.code = function code(_code, infostring, escaped) { + var lang = (infostring || '').match(/\S*/)[0]; -InlineLexer.escapes = function(text) { - return text ? text.replace(InlineLexer.rules._escapes, '$1') : text; -}; + if (this.options.highlight) { + var out = this.options.highlight(_code, lang); -/** - * Compile Link - */ + if (out != null && out !== _code) { + escaped = true; + _code = out; + } + } -InlineLexer.prototype.outputLink = function(cap, link) { - var href = link.href, - title = link.title ? escape(link.title) : null; + if (!lang) { + return '
' + (escaped ? _code : escape$1(_code, true)) + '
\n'; + } - return cap[0].charAt(0) !== '!' - ? this.renderer.link(href, title, this.output(cap[1])) - : this.renderer.image(href, title, escape(cap[1])); -}; + return '
' + (escaped ? _code : escape$1(_code, true)) + '
\n'; + }; -/** - * Smartypants Transformations - */ + _proto.blockquote = function blockquote(quote) { + return '
\n' + quote + '
\n'; + }; -InlineLexer.prototype.smartypants = function(text) { - if (!this.options.smartypants) return text; - return text - // em-dashes - .replace(/---/g, '\u2014') - // en-dashes - .replace(/--/g, '\u2013') - // opening singles - .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018') - // closing singles & apostrophes - .replace(/'/g, '\u2019') - // opening doubles - .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c') - // closing doubles - .replace(/"/g, '\u201d') - // ellipses - .replace(/\.{3}/g, '\u2026'); -}; + _proto.html = function html(_html) { + return _html; + }; -/** - * Mangle Links - */ + _proto.heading = function heading(text, level, raw, slugger) { + if (this.options.headerIds) { + return '' + text + '\n'; + } // ignore IDs -InlineLexer.prototype.mangle = function(text) { - if (!this.options.mangle) return text; - var out = '', - l = text.length, - i = 0, - ch; - for (; i < l; i++) { - ch = text.charCodeAt(i); - if (Math.random() > 0.5) { - ch = 'x' + ch.toString(16); - } - out += '&#' + ch + ';'; - } + return '' + text + '\n'; + }; - return out; -}; + _proto.hr = function hr() { + return this.options.xhtml ? '
\n' : '
\n'; + }; -/** - * Renderer - */ + _proto.list = function list(body, ordered, start) { + var type = ordered ? 'ol' : 'ul', + startatt = ordered && start !== 1 ? ' start="' + start + '"' : ''; + return '<' + type + startatt + '>\n' + body + '\n'; + }; -function Renderer(options) { - this.options = options || marked.defaults; -} + _proto.listitem = function listitem(text) { + return '
  • ' + text + '
  • \n'; + }; -Renderer.prototype.code = function(code, infostring, escaped) { - var lang = (infostring || '').match(/\S*/)[0]; - if (this.options.highlight) { - var out = this.options.highlight(code, lang); - if (out != null && out !== code) { - escaped = true; - code = out; - } - } + _proto.checkbox = function checkbox(checked) { + return ' '; + }; - if (!lang) { - return '
    '
    -      + (escaped ? code : escape(code, true))
    -      + '
    '; - } + _proto.paragraph = function paragraph(text) { + return '

    ' + text + '

    \n'; + }; - return '
    '
    -    + (escaped ? code : escape(code, true))
    -    + '
    \n'; -}; + _proto.table = function table(header, body) { + if (body) body = '' + body + ''; + return '\n' + '\n' + header + '\n' + body + '
    \n'; + }; -Renderer.prototype.blockquote = function(quote) { - return '
    \n' + quote + '
    \n'; -}; + _proto.tablerow = function tablerow(content) { + return '\n' + content + '\n'; + }; -Renderer.prototype.html = function(html) { - return html; -}; + _proto.tablecell = function tablecell(content, flags) { + var type = flags.header ? 'th' : 'td'; + var tag = flags.align ? '<' + type + ' align="' + flags.align + '">' : '<' + type + '>'; + return tag + content + '\n'; + } // span level renderer + ; -Renderer.prototype.heading = function(text, level, raw, slugger) { - if (this.options.headerIds) { - return '' - + text - + '\n'; - } - // ignore IDs - return '' + text + '\n'; -}; + _proto.strong = function strong(text) { + return '' + text + ''; + }; -Renderer.prototype.hr = function() { - return this.options.xhtml ? '
    \n' : '
    \n'; -}; + _proto.em = function em(text) { + return '' + text + ''; + }; -Renderer.prototype.list = function(body, ordered, start) { - var type = ordered ? 'ol' : 'ul', - startatt = (ordered && start !== 1) ? (' start="' + start + '"') : ''; - return '<' + type + startatt + '>\n' + body + '\n'; -}; + _proto.codespan = function codespan(text) { + return '' + text + ''; + }; -Renderer.prototype.listitem = function(text) { - return '
  • ' + text + '
  • \n'; -}; + _proto.br = function br() { + return this.options.xhtml ? '
    ' : '
    '; + }; -Renderer.prototype.checkbox = function(checked) { - return ' '; -}; + _proto.del = function del(text) { + return '' + text + ''; + }; -Renderer.prototype.paragraph = function(text) { - return '

    ' + text + '

    \n'; -}; + _proto.link = function link(href, title, text) { + href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href); -Renderer.prototype.table = function(header, body) { - if (body) body = '' + body + ''; + if (href === null) { + return text; + } - return '\n' - + '\n' - + header - + '\n' - + body - + '
    \n'; -}; + var out = '
    \n' + content + '\n'; -}; + if (title) { + out += ' title="' + title + '"'; + } -Renderer.prototype.tablecell = function(content, flags) { - var type = flags.header ? 'th' : 'td'; - var tag = flags.align - ? '<' + type + ' align="' + flags.align + '">' - : '<' + type + '>'; - return tag + content + '\n'; -}; + out += '>' + text + ''; + return out; + }; -// span level renderer -Renderer.prototype.strong = function(text) { - return '' + text + ''; -}; + _proto.image = function image(href, title, text) { + href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href); -Renderer.prototype.em = function(text) { - return '' + text + ''; -}; + if (href === null) { + return text; + } -Renderer.prototype.codespan = function(text) { - return '' + text + ''; -}; + var out = '' + text + '' : '
    '; -}; + if (title) { + out += ' title="' + title + '"'; + } -Renderer.prototype.del = function(text) { - return '' + text + ''; -}; + out += this.options.xhtml ? '/>' : '>'; + return out; + }; -Renderer.prototype.link = function(href, title, text) { - href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); - if (href === null) { - return text; - } - var out = ''; - return out; -}; + _proto.text = function text(_text) { + return _text; + }; -Renderer.prototype.image = function(href, title, text) { - href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); - if (href === null) { - return text; - } + return Renderer; + }(); - var out = '' + text + '' : '>'; - return out; -}; + /** + * TextRenderer + * returns only the textual part of the token + */ + var TextRenderer_1 = /*#__PURE__*/function () { + function TextRenderer() {} -Renderer.prototype.text = function(text) { - return text; -}; + var _proto = TextRenderer.prototype; -/** - * TextRenderer - * returns only the textual part of the token - */ + // no need for block level renderers + _proto.strong = function strong(text) { + return text; + }; -function TextRenderer() {} + _proto.em = function em(text) { + return text; + }; -// no need for block level renderers + _proto.codespan = function codespan(text) { + return text; + }; -TextRenderer.prototype.strong = -TextRenderer.prototype.em = -TextRenderer.prototype.codespan = -TextRenderer.prototype.del = -TextRenderer.prototype.text = function (text) { - return text; -}; + _proto.del = function del(text) { + return text; + }; -TextRenderer.prototype.link = -TextRenderer.prototype.image = function(href, title, text) { - return '' + text; -}; + _proto.html = function html(text) { + return text; + }; -TextRenderer.prototype.br = function() { - return ''; -}; + _proto.text = function text(_text) { + return _text; + }; -/** - * Parsing & Compiling - */ + _proto.link = function link(href, title, text) { + return '' + text; + }; -function Parser(options) { - this.tokens = []; - this.token = null; - this.options = options || marked.defaults; - this.options.renderer = this.options.renderer || new Renderer(); - this.renderer = this.options.renderer; - this.renderer.options = this.options; - this.slugger = new Slugger(); -} + _proto.image = function image(href, title, text) { + return '' + text; + }; -/** - * Static Parse Method - */ - -Parser.parse = function(src, options) { - var parser = new Parser(options); - return parser.parse(src); -}; - -/** - * Parse Loop - */ - -Parser.prototype.parse = function(src) { - this.inline = new InlineLexer(src.links, this.options); - // use an InlineLexer with a TextRenderer to extract pure text - this.inlineText = new InlineLexer( - src.links, - merge({}, this.options, {renderer: new TextRenderer()}) - ); - this.tokens = src.reverse(); - - var out = ''; - while (this.next()) { - out += this.tok(); - } - - return out; -}; - -/** - * Next Token - */ - -Parser.prototype.next = function() { - return this.token = this.tokens.pop(); -}; - -/** - * Preview Next Token - */ - -Parser.prototype.peek = function() { - return this.tokens[this.tokens.length - 1] || 0; -}; - -/** - * Parse Text Tokens - */ - -Parser.prototype.parseText = function() { - var body = this.token.text; - - while (this.peek().type === 'text') { - body += '\n' + this.next().text; - } - - return this.inline.output(body); -}; - -/** - * Parse Current Token - */ - -Parser.prototype.tok = function() { - switch (this.token.type) { - case 'space': { + _proto.br = function br() { return ''; + }; + + return TextRenderer; + }(); + + /** + * Slugger generates header id + */ + var Slugger_1 = /*#__PURE__*/function () { + function Slugger() { + this.seen = {}; } - case 'hr': { - return this.renderer.hr(); + + var _proto = Slugger.prototype; + + _proto.serialize = function serialize(value) { + return value.toLowerCase().trim() // remove html tags + .replace(/<[!\/a-z].*?>/ig, '') // remove unwanted chars + .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '').replace(/\s/g, '-'); } - case 'heading': { - return this.renderer.heading( - this.inline.output(this.token.text), - this.token.depth, - unescape(this.inlineText.output(this.token.text)), - this.slugger); + /** + * Finds the next safe (unique) slug to use + */ + ; + + _proto.getNextSafeSlug = function getNextSafeSlug(originalSlug, isDryRun) { + var slug = originalSlug; + var occurenceAccumulator = 0; + + if (this.seen.hasOwnProperty(slug)) { + occurenceAccumulator = this.seen[originalSlug]; + + do { + occurenceAccumulator++; + slug = originalSlug + '-' + occurenceAccumulator; + } while (this.seen.hasOwnProperty(slug)); + } + + if (!isDryRun) { + this.seen[originalSlug] = occurenceAccumulator; + this.seen[slug] = 0; + } + + return slug; } - case 'code': { - return this.renderer.code(this.token.text, - this.token.lang, - this.token.escaped); + /** + * Convert string to unique id + * @param {object} options + * @param {boolean} options.dryrun Generates the next unique slug without updating the internal accumulator. + */ + ; + + _proto.slug = function slug(value, options) { + if (options === void 0) { + options = {}; + } + + var slug = this.serialize(value); + return this.getNextSafeSlug(slug, options.dryrun); + }; + + return Slugger; + }(); + + var defaults$4 = defaults.defaults; + var unescape$1 = helpers.unescape; + /** + * Parsing & Compiling + */ + + var Parser_1 = /*#__PURE__*/function () { + function Parser(options) { + this.options = options || defaults$4; + this.options.renderer = this.options.renderer || new Renderer_1(); + this.renderer = this.options.renderer; + this.renderer.options = this.options; + this.textRenderer = new TextRenderer_1(); + this.slugger = new Slugger_1(); } - case 'table': { - var header = '', - body = '', + /** + * Static Parse Method + */ + + + Parser.parse = function parse(tokens, options) { + var parser = new Parser(options); + return parser.parse(tokens); + } + /** + * Parse Loop + */ + ; + + var _proto = Parser.prototype; + + _proto.parse = function parse(tokens, top) { + if (top === void 0) { + top = true; + } + + var out = '', i, + j, + k, + l2, + l3, row, cell, - j; + header, + body, + token, + ordered, + start, + loose, + itemBody, + item, + checked, + task, + checkbox; + var l = tokens.length; - // header - cell = ''; - for (i = 0; i < this.token.header.length; i++) { - cell += this.renderer.tablecell( - this.inline.output(this.token.header[i]), - { header: true, align: this.token.align[i] } - ); - } - header += this.renderer.tablerow(cell); + for (i = 0; i < l; i++) { + token = tokens[i]; - for (i = 0; i < this.token.cells.length; i++) { - row = this.token.cells[i]; + switch (token.type) { + case 'space': + { + continue; + } - cell = ''; - for (j = 0; j < row.length; j++) { - cell += this.renderer.tablecell( - this.inline.output(row[j]), - { header: false, align: this.token.align[j] } - ); + case 'hr': + { + out += this.renderer.hr(); + continue; + } + + case 'heading': + { + out += this.renderer.heading(this.parseInline(token.tokens), token.depth, unescape$1(this.parseInline(token.tokens, this.textRenderer)), this.slugger); + continue; + } + + case 'code': + { + out += this.renderer.code(token.text, token.lang, token.escaped); + continue; + } + + case 'table': + { + header = ''; // header + + cell = ''; + l2 = token.header.length; + + for (j = 0; j < l2; j++) { + cell += this.renderer.tablecell(this.parseInline(token.tokens.header[j]), { + header: true, + align: token.align[j] + }); + } + + header += this.renderer.tablerow(cell); + body = ''; + l2 = token.cells.length; + + for (j = 0; j < l2; j++) { + row = token.tokens.cells[j]; + cell = ''; + l3 = row.length; + + for (k = 0; k < l3; k++) { + cell += this.renderer.tablecell(this.parseInline(row[k]), { + header: false, + align: token.align[k] + }); + } + + body += this.renderer.tablerow(cell); + } + + out += this.renderer.table(header, body); + continue; + } + + case 'blockquote': + { + body = this.parse(token.tokens); + out += this.renderer.blockquote(body); + continue; + } + + case 'list': + { + ordered = token.ordered; + start = token.start; + loose = token.loose; + l2 = token.items.length; + body = ''; + + for (j = 0; j < l2; j++) { + item = token.items[j]; + checked = item.checked; + task = item.task; + itemBody = ''; + + if (item.task) { + checkbox = this.renderer.checkbox(checked); + + if (loose) { + if (item.tokens.length > 0 && item.tokens[0].type === 'text') { + item.tokens[0].text = checkbox + ' ' + item.tokens[0].text; + + if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') { + item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text; + } + } else { + item.tokens.unshift({ + type: 'text', + text: checkbox + }); + } + } else { + itemBody += checkbox; + } + } + + itemBody += this.parse(item.tokens, loose); + body += this.renderer.listitem(itemBody, task, checked); + } + + out += this.renderer.list(body, ordered, start); + continue; + } + + case 'html': + { + // TODO parse inline content if parameter markdown=1 + out += this.renderer.html(token.text); + continue; + } + + case 'paragraph': + { + out += this.renderer.paragraph(this.parseInline(token.tokens)); + continue; + } + + case 'text': + { + body = token.tokens ? this.parseInline(token.tokens) : token.text; + + while (i + 1 < l && tokens[i + 1].type === 'text') { + token = tokens[++i]; + body += '\n' + (token.tokens ? this.parseInline(token.tokens) : token.text); + } + + out += top ? this.renderer.paragraph(body) : body; + continue; + } + + default: + { + var errMsg = 'Token with "' + token.type + '" type was not found.'; + + if (this.options.silent) { + console.error(errMsg); + return; + } else { + throw new Error(errMsg); + } + } } - - body += this.renderer.tablerow(cell); - } - return this.renderer.table(header, body); - } - case 'blockquote_start': { - body = ''; - - while (this.next().type !== 'blockquote_end') { - body += this.tok(); } - return this.renderer.blockquote(body); + return out; } - case 'list_start': { - body = ''; - var ordered = this.token.ordered, - start = this.token.start; + /** + * Parse Inline Tokens + */ + ; - while (this.next().type !== 'list_end') { - body += this.tok(); - } + _proto.parseInline = function parseInline(tokens, renderer) { + renderer = renderer || this.renderer; + var out = '', + i, + token; + var l = tokens.length; - return this.renderer.list(body, ordered, start); - } - case 'list_item_start': { - body = ''; - var loose = this.token.loose; - var checked = this.token.checked; - var task = this.token.task; + for (i = 0; i < l; i++) { + token = tokens[i]; - if (this.token.task) { - body += this.renderer.checkbox(checked); - } + switch (token.type) { + case 'escape': + { + out += renderer.text(token.text); + break; + } - while (this.next().type !== 'list_item_end') { - body += !loose && this.token.type === 'text' - ? this.parseText() - : this.tok(); - } - return this.renderer.listitem(body, task, checked); - } - case 'html': { - // TODO parse inline content if parameter markdown=1 - return this.renderer.html(this.token.text); - } - case 'paragraph': { - return this.renderer.paragraph(this.inline.output(this.token.text)); - } - case 'text': { - return this.renderer.paragraph(this.parseText()); - } - default: { - var errMsg = 'Token with "' + this.token.type + '" type was not found.'; - if (this.options.silent) { - console.log(errMsg); - } else { - throw new Error(errMsg); - } - } - } -}; + case 'html': + { + out += renderer.html(token.text); + break; + } -/** - * Slugger generates header id - */ + case 'link': + { + out += renderer.link(token.href, token.title, this.parseInline(token.tokens, renderer)); + break; + } -function Slugger () { - this.seen = {}; -} + case 'image': + { + out += renderer.image(token.href, token.title, token.text); + break; + } -/** - * Convert string to unique id - */ + case 'strong': + { + out += renderer.strong(this.parseInline(token.tokens, renderer)); + break; + } -Slugger.prototype.slug = function (value) { - var slug = value - .toLowerCase() - .trim() - .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '') - .replace(/\s/g, '-'); + case 'em': + { + out += renderer.em(this.parseInline(token.tokens, renderer)); + break; + } - if (this.seen.hasOwnProperty(slug)) { - var originalSlug = slug; - do { - this.seen[originalSlug]++; - slug = originalSlug + '-' + this.seen[originalSlug]; - } while (this.seen.hasOwnProperty(slug)); - } - this.seen[slug] = 0; + case 'codespan': + { + out += renderer.codespan(token.text); + break; + } - return slug; -}; + case 'br': + { + out += renderer.br(); + break; + } -/** - * Helpers - */ + case 'del': + { + out += renderer.del(this.parseInline(token.tokens, renderer)); + break; + } -function escape(html, encode) { - if (encode) { - if (escape.escapeTest.test(html)) { - return html.replace(escape.escapeReplace, function (ch) { return escape.replacements[ch]; }); - } - } else { - if (escape.escapeTestNoEncode.test(html)) { - return html.replace(escape.escapeReplaceNoEncode, function (ch) { return escape.replacements[ch]; }); - } - } + case 'text': + { + out += renderer.text(token.text); + break; + } - return html; -} + default: + { + var errMsg = 'Token with "' + token.type + '" type was not found.'; -escape.escapeTest = /[&<>"']/; -escape.escapeReplace = /[&<>"']/g; -escape.replacements = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''' -}; - -escape.escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/; -escape.escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g; - -function unescape(html) { - // explicitly match decimal, hex, and named HTML entities - return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig, function(_, n) { - n = n.toLowerCase(); - if (n === 'colon') return ':'; - if (n.charAt(0) === '#') { - return n.charAt(1) === 'x' - ? String.fromCharCode(parseInt(n.substring(2), 16)) - : String.fromCharCode(+n.substring(1)); - } - return ''; - }); -} - -function edit(regex, opt) { - regex = regex.source || regex; - opt = opt || ''; - return { - replace: function(name, val) { - val = val.source || val; - val = val.replace(/(^|[^\[])\^/g, '$1'); - regex = regex.replace(name, val); - return this; - }, - getRegex: function() { - return new RegExp(regex, opt); - } - }; -} - -function cleanUrl(sanitize, base, href) { - if (sanitize) { - try { - var prot = decodeURIComponent(unescape(href)) - .replace(/[^\w:]/g, '') - .toLowerCase(); - } catch (e) { - return null; - } - if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) { - return null; - } - } - if (base && !originIndependentUrl.test(href)) { - href = resolveUrl(base, href); - } - try { - href = encodeURI(href).replace(/%25/g, '%'); - } catch (e) { - return null; - } - return href; -} - -function resolveUrl(base, href) { - if (!baseUrls[' ' + base]) { - // we can ignore everything in base after the last slash of its path component, - // but we might need to add _that_ - // https://tools.ietf.org/html/rfc3986#section-3 - if (/^[^:]+:\/*[^/]*$/.test(base)) { - baseUrls[' ' + base] = base + '/'; - } else { - baseUrls[' ' + base] = rtrim(base, '/', true); - } - } - base = baseUrls[' ' + base]; - - if (href.slice(0, 2) === '//') { - return base.replace(/:[\s\S]*/, ':') + href; - } else if (href.charAt(0) === '/') { - return base.replace(/(:\/*[^/]*)[\s\S]*/, '$1') + href; - } else { - return base + href; - } -} -var baseUrls = {}; -var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i; - -function noop() {} -noop.exec = noop; - -function merge(obj) { - var i = 1, - target, - key; - - for (; i < arguments.length; i++) { - target = arguments[i]; - for (key in target) { - if (Object.prototype.hasOwnProperty.call(target, key)) { - obj[key] = target[key]; - } - } - } - - return obj; -} - -function splitCells(tableRow, count) { - // ensure that every cell-delimiting pipe has a space - // before it to distinguish it from an escaped pipe - var row = tableRow.replace(/\|/g, function (match, offset, str) { - var escaped = false, - curr = offset; - while (--curr >= 0 && str[curr] === '\\') escaped = !escaped; - if (escaped) { - // odd number of slashes means | is escaped - // so we leave it alone - return '|'; - } else { - // add space before unescaped | - return ' |'; + if (this.options.silent) { + console.error(errMsg); + return; + } else { + throw new Error(errMsg); + } + } } - }), - cells = row.split(/ \|/), - i = 0; - - if (cells.length > count) { - cells.splice(count); - } else { - while (cells.length < count) cells.push(''); - } - - for (; i < cells.length; i++) { - // leading or trailing whitespace is ignored per the gfm spec - cells[i] = cells[i].trim().replace(/\\\|/g, '|'); - } - return cells; -} - -// Remove trailing 'c's. Equivalent to str.replace(/c*$/, ''). -// /c*$/ is vulnerable to REDOS. -// invert: Remove suffix of non-c chars instead. Default falsey. -function rtrim(str, c, invert) { - if (str.length === 0) { - return ''; - } - - // Length of suffix matching the invert condition. - var suffLen = 0; - - // Step left until we fail to match the invert condition. - while (suffLen < str.length) { - var currChar = str.charAt(str.length - suffLen - 1); - if (currChar === c && !invert) { - suffLen++; - } else if (currChar !== c && invert) { - suffLen++; - } else { - break; - } - } - - return str.substr(0, str.length - suffLen); -} - -function findClosingBracket(str, b) { - if (str.indexOf(b[1]) === -1) { - return -1; - } - var level = 0; - for (var i = 0; i < str.length; i++) { - if (str[i] === '\\') { - i++; - } else if (str[i] === b[0]) { - level++; - } else if (str[i] === b[1]) { - level--; - if (level < 0) { - return i; } + + return out; + }; + + return Parser; + }(); + + var merge$2 = helpers.merge, + checkSanitizeDeprecation$1 = helpers.checkSanitizeDeprecation, + escape$2 = helpers.escape; + var getDefaults = defaults.getDefaults, + changeDefaults = defaults.changeDefaults, + defaults$5 = defaults.defaults; + /** + * Marked + */ + + function marked(src, opt, callback) { + // throw error in case of non string input + if (typeof src === 'undefined' || src === null) { + throw new Error('marked(): input parameter is undefined or null'); } - } - return -1; -} -/** - * Marked - */ + if (typeof src !== 'string') { + throw new Error('marked(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected'); + } -function marked(src, opt, callback) { - // throw error in case of non string input - if (typeof src === 'undefined' || src === null) { - throw new Error('marked(): input parameter is undefined or null'); - } - if (typeof src !== 'string') { - throw new Error('marked(): input parameter is of type ' - + Object.prototype.toString.call(src) + ', string expected'); - } - - if (callback || typeof opt === 'function') { - if (!callback) { + if (typeof opt === 'function') { callback = opt; opt = null; } - opt = merge({}, marked.defaults, opt || {}); + opt = merge$2({}, marked.defaults, opt || {}); + checkSanitizeDeprecation$1(opt); - var highlight = opt.highlight, - tokens, - pending, - i = 0; - - try { - tokens = Lexer.lex(src, opt); - } catch (e) { - return callback(e); - } - - pending = tokens.length; - - var done = function(err) { - if (err) { - opt.highlight = highlight; - return callback(err); - } - - var out; + if (callback) { + var highlight = opt.highlight; + var tokens; try { - out = Parser.parse(tokens, opt); + tokens = Lexer_1.lex(src, opt); } catch (e) { - err = e; + return callback(e); } - opt.highlight = highlight; + var done = function done(err) { + var out; - return err - ? callback(err) - : callback(null, out); - }; - - if (!highlight || highlight.length < 3) { - return done(); - } - - delete opt.highlight; - - if (!pending) return done(); - - for (; i < tokens.length; i++) { - (function(token) { - if (token.type !== 'code') { - return --pending || done(); - } - return highlight(token.text, token.lang, function(err, code) { - if (err) return done(err); - if (code == null || code === token.text) { - return --pending || done(); + if (!err) { + try { + out = Parser_1.parse(tokens, opt); + } catch (e) { + err = e; } - token.text = code; - token.escaped = true; - --pending || done(); - }); - })(tokens[i]); + } + + opt.highlight = highlight; + return err ? callback(err) : callback(null, out); + }; + + if (!highlight || highlight.length < 3) { + return done(); + } + + delete opt.highlight; + if (!tokens.length) return done(); + var pending = 0; + marked.walkTokens(tokens, function (token) { + if (token.type === 'code') { + pending++; + setTimeout(function () { + highlight(token.text, token.lang, function (err, code) { + if (err) { + return done(err); + } + + if (code != null && code !== token.text) { + token.text = code; + token.escaped = true; + } + + pending--; + + if (pending === 0) { + done(); + } + }); + }, 0); + } + }); + + if (pending === 0) { + done(); + } + + return; } - return; - } - try { - if (opt) opt = merge({}, marked.defaults, opt); - return Parser.parse(Lexer.lex(src, opt), opt); - } catch (e) { - e.message += '\nPlease report this to https://github.com/markedjs/marked.'; - if ((opt || marked.defaults).silent) { - return '

    An error occurred:

    '
    -        + escape(e.message + '', true)
    -        + '
    '; + try { + var _tokens = Lexer_1.lex(src, opt); + + if (opt.walkTokens) { + marked.walkTokens(_tokens, opt.walkTokens); + } + + return Parser_1.parse(_tokens, opt); + } catch (e) { + e.message += '\nPlease report this to https://github.com/markedjs/marked.'; + + if (opt.silent) { + return '

    An error occurred:

    ' + escape$2(e.message + '', true) + '
    '; + } + + throw e; } - throw e; } -} + /** + * Options + */ -/** - * Options - */ -marked.options = -marked.setOptions = function(opt) { - merge(marked.defaults, opt); - return marked; -}; - -marked.getDefaults = function () { - return { - baseUrl: null, - breaks: false, - gfm: true, - headerIds: true, - headerPrefix: '', - highlight: null, - langPrefix: 'language-', - mangle: true, - pedantic: false, - renderer: new Renderer(), - sanitize: false, - sanitizer: null, - silent: false, - smartLists: false, - smartypants: false, - tables: true, - xhtml: false + marked.options = marked.setOptions = function (opt) { + merge$2(marked.defaults, opt); + changeDefaults(marked.defaults); + return marked; }; -}; -marked.defaults = marked.getDefaults(); + marked.getDefaults = getDefaults; + marked.defaults = defaults$5; + /** + * Use Extension + */ -/** - * Expose - */ + marked.use = function (extension) { + var opts = merge$2({}, extension); -marked.Parser = Parser; -marked.parser = Parser.parse; + if (extension.renderer) { + (function () { + var renderer = marked.defaults.renderer || new Renderer_1(); -marked.Renderer = Renderer; -marked.TextRenderer = TextRenderer; + var _loop = function _loop(prop) { + var prevRenderer = renderer[prop]; -marked.Lexer = Lexer; -marked.lexer = Lexer.lex; + renderer[prop] = function () { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } -marked.InlineLexer = InlineLexer; -marked.inlineLexer = InlineLexer.output; + var ret = extension.renderer[prop].apply(renderer, args); -marked.Slugger = Slugger; + if (ret === false) { + ret = prevRenderer.apply(renderer, args); + } -marked.parse = marked; + return ret; + }; + }; -// BEGIN MONACOCHANGE -// if (typeof module !== 'undefined' && typeof exports === 'object') { -// module.exports = marked; -// } else if (typeof define === 'function' && define.amd) { -// define(function() { return marked; }); -// } else { -// root.marked = marked; -// } -// })(this || (typeof window !== 'undefined' ? window : global)); -__marked_exports = marked; -}).call(this); + for (var prop in extension.renderer) { + _loop(prop); + } -// ESM-comment-begin -define(function() { return __marked_exports; }); -// ESM-comment-end + opts.renderer = renderer; + })(); + } -// ESM-uncomment-begin -// export var marked = __marked_exports; -// export var Parser = __marked_exports.Parser; -// export var parser = __marked_exports.parser; -// export var Renderer = __marked_exports.Renderer; -// export var TextRenderer = __marked_exports.TextRenderer; -// export var Lexer = __marked_exports.Lexer; -// export var lexer = __marked_exports.lexer; -// export var InlineLexer = __marked_exports.InlineLexer; -// export var inlineLexer = __marked_exports.inlineLexer; -// export var parse = __marked_exports.parse; -// ESM-uncomment-end -// END MONACOCHANGE + if (extension.tokenizer) { + (function () { + var tokenizer = marked.defaults.tokenizer || new Tokenizer_1(); + + var _loop2 = function _loop2(prop) { + var prevTokenizer = tokenizer[prop]; + + tokenizer[prop] = function () { + for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + + var ret = extension.tokenizer[prop].apply(tokenizer, args); + + if (ret === false) { + ret = prevTokenizer.apply(tokenizer, args); + } + + return ret; + }; + }; + + for (var prop in extension.tokenizer) { + _loop2(prop); + } + + opts.tokenizer = tokenizer; + })(); + } + + if (extension.walkTokens) { + var walkTokens = marked.defaults.walkTokens; + + opts.walkTokens = function (token) { + extension.walkTokens(token); + + if (walkTokens) { + walkTokens(token); + } + }; + } + + marked.setOptions(opts); + }; + /** + * Run callback for every token + */ + + + marked.walkTokens = function (tokens, callback) { + for (var _iterator = _createForOfIteratorHelperLoose(tokens), _step; !(_step = _iterator()).done;) { + var token = _step.value; + callback(token); + + switch (token.type) { + case 'table': + { + for (var _iterator2 = _createForOfIteratorHelperLoose(token.tokens.header), _step2; !(_step2 = _iterator2()).done;) { + var cell = _step2.value; + marked.walkTokens(cell, callback); + } + + for (var _iterator3 = _createForOfIteratorHelperLoose(token.tokens.cells), _step3; !(_step3 = _iterator3()).done;) { + var row = _step3.value; + + for (var _iterator4 = _createForOfIteratorHelperLoose(row), _step4; !(_step4 = _iterator4()).done;) { + var _cell = _step4.value; + marked.walkTokens(_cell, callback); + } + } + + break; + } + + case 'list': + { + marked.walkTokens(token.items, callback); + break; + } + + default: + { + if (token.tokens) { + marked.walkTokens(token.tokens, callback); + } + } + } + } + }; + /** + * Expose + */ + + + marked.Parser = Parser_1; + marked.parser = Parser_1.parse; + marked.Renderer = Renderer_1; + marked.TextRenderer = TextRenderer_1; + marked.Lexer = Lexer_1; + marked.lexer = Lexer_1.lex; + marked.Tokenizer = Tokenizer_1; + marked.Slugger = Slugger_1; + marked.parse = marked; + var marked_1 = marked; + + return marked_1; + +}))); diff --git a/src/vs/base/test/browser/markdownRenderer.test.ts b/src/vs/base/test/browser/markdownRenderer.test.ts index 6ec7827c752..78f82030b2c 100644 --- a/src/vs/base/test/browser/markdownRenderer.test.ts +++ b/src/vs/base/test/browser/markdownRenderer.test.ts @@ -18,7 +18,6 @@ suite('MarkdownRenderer', () => { const result: HTMLElement = renderMarkdown(markdown); const renderer = new marked.Renderer(); const imageFromMarked = marked(markdown.value, { - sanitize: true, renderer }).trim(); assert.strictEqual(result.innerHTML, imageFromMarked); @@ -29,7 +28,6 @@ suite('MarkdownRenderer', () => { const result: HTMLElement = renderMarkdown(markdown); const renderer = new marked.Renderer(); const imageFromMarked = marked(markdown.value, { - sanitize: true, renderer }).trim(); assert.strictEqual(result.innerHTML, imageFromMarked); From 4585a8cc1d58902fdf626797610ffeba9b89921c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 11 Aug 2020 16:57:22 -0700 Subject: [PATCH 130/736] Disable assertion around mailto The new version of marked encodes the uri differently that causes this test to fail --- src/vs/workbench/test/browser/api/extHostTypeConverter.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/test/browser/api/extHostTypeConverter.test.ts b/src/vs/workbench/test/browser/api/extHostTypeConverter.test.ts index a14517bfff2..feb4e1dd1ed 100644 --- a/src/vs/workbench/test/browser/api/extHostTypeConverter.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTypeConverter.test.ts @@ -45,7 +45,7 @@ suite('ExtHostTypeConverter', function () { data = MarkdownString.from('hello@foo.bar'); assert.equal(data.value, 'hello@foo.bar'); assert.equal(size(data.uris!), 1); - assert.ok(!!data.uris!['mailto:hello@foo.bar']); + // assert.ok(!!data.uris!['mailto:hello@foo.bar']); data = MarkdownString.from('*hello* [click](command:me)'); assert.equal(data.value, '*hello* [click](command:me)'); From ce508a8214dc66f5a91ae2229f7b67263a2f7a54 Mon Sep 17 00:00:00 2001 From: kieferrm Date: Wed, 12 Aug 2020 00:44:36 +0000 Subject: [PATCH 131/736] fix GDPR annotation --- .../typescript-language-features/src/tsServer/serverError.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/tsServer/serverError.ts b/extensions/typescript-language-features/src/tsServer/serverError.ts index 42622669498..2653360c9be 100644 --- a/extensions/typescript-language-features/src/tsServer/serverError.ts +++ b/extensions/typescript-language-features/src/tsServer/serverError.ts @@ -39,7 +39,7 @@ export class TypeScriptServerError extends Error { "TypeScriptRequestErrorProperties" : { "command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "serverid" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "sanitizedstack" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "sanitizedstack" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } } */ return { From c17b4c2488edd0f4f2c855b96cc2d8d8a1029a64 Mon Sep 17 00:00:00 2001 From: kieferrm Date: Wed, 12 Aug 2020 00:47:16 +0000 Subject: [PATCH 132/736] fix GDPR annotations --- .../src/languageFeatures/completions.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/completions.ts b/extensions/typescript-language-features/src/languageFeatures/completions.ts index 5c43857fbee..96914bc83a7 100644 --- a/extensions/typescript-language-features/src/languageFeatures/completions.ts +++ b/extensions/typescript-language-features/src/languageFeatures/completions.ts @@ -338,7 +338,7 @@ class CompletionAcceptedCommand implements Command { if (item instanceof MyCompletionItem) { /* __GDPR__ "completions.accept" : { - "isPackageJsonImport" : { "classification": "SystemMetadata", "purpose": "FeatureInsight" }, + "isPackageJsonImport" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "${include}": [ "${TypeScriptCommonProperties}" ] @@ -555,12 +555,12 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider< ) { /* __GDPR__ "completions.execute" : { - "duration" : { "classification": "SystemMetadata", "purpose": "FeatureInsight" }, - "type" : { "classification": "SystemMetadata", "purpose": "FeatureInsight" }, - "count" : { "classification": "SystemMetadata", "purpose": "FeatureInsight" }, - "updateGraphDurationMs" : { "classification": "SystemMetadata", "purpose": "FeatureInsight" }, - "createAutoImportProviderProgramDurationMs" : { "classification": "SystemMetadata", "purpose": "FeatureInsight" }, - "includesPackageJsonImport" : { "classification": "SystemMetadata", "purpose": "FeatureInsight" }, + "duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "updateGraphDurationMs" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "createAutoImportProviderProgramDurationMs" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "includesPackageJsonImport" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "${include}": [ "${TypeScriptCommonProperties}" ] From 12ff03854863486311aac5229b4b98ca610fb3f2 Mon Sep 17 00:00:00 2001 From: orionlee Date: Tue, 11 Aug 2020 21:01:51 -0700 Subject: [PATCH 133/736] Dimmed Monokai theme - improve markdown support - redner bold/italic , link title color --- .../themes/dimmed-monokai-color-theme.json | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json index 935573463ee..5140f5ad3d0 100644 --- a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json +++ b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json @@ -557,6 +557,65 @@ "foreground": "#D0B344" } }, + { + "name": "Markdown Headings", + "scope": "markup.heading.markdown", + "settings": { + "fontStyle": "bold" + } + }, + { + "name": "Markdown Quote", + "scope": "markup.quote.markdown", + "settings": { + "fontStyle": "italic", + "foreground": "" + } + }, + { + "name": "Markdown Bold", + "scope": "markup.bold.markdown", + "settings": { + "fontStyle": "bold" + } + }, + { + "name": "Markdown Link Title/Description", + "scope": "string.other.link.title.markdown,string.other.link.description.markdown", + "settings": { + "foreground": "#AE81FF" + } + }, + { + "name": "Markdown Underline Link/Image", + "scope": "markup.underline.link.markdown,markup.underline.link.image.markdown", + "settings": { + "foreground": "" + } + }, + { + "name": "Markdown Emphasis", + "scope": "markup.italic.markdown", + "settings": { + "fontStyle": "italic" + } + }, + { + "name": "Markdown Punctuation Definition Link", + "scope": "markup.list.unnumbered.markdown, markup.list.numbered.markdown", + "settings": { + "foreground": "" + } + }, + { + "name": "Markdown List Punctuation", + "scope": [ + "punctuation.definition.list.begin.markdown" + ], + "settings": { + "foreground": "" + } + }, { "scope": "token.info-token", "settings": { From bae64052f49e00bd30da80720bfcf4e9609b1827 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 12 Aug 2020 09:05:18 +0200 Subject: [PATCH 134/736] unit test for document remove --- .../api/browser/mainThreadNotebook.ts | 2 +- .../workbench/api/common/extHost.protocol.ts | 2 +- .../workbench/api/common/extHostNotebook.ts | 2 +- .../test/browser/api/extHostNotebook.test.ts | 49 +++++++++++++++++-- .../api/extHostNotebookConcatDocument.test.ts | 4 +- 5 files changed, 50 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index ceed17dc7b2..d9a61aa4f42 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -203,7 +203,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo async removeNotebookTextModel(uri: URI): Promise { // TODO@rebornix, remove cell should use emitDelta as well to ensure document/editor events are sent together - await this._proxy.$acceptDocumentAndEditorsDelta({ removedDocuments: [uri] }); + this._proxy.$acceptDocumentAndEditorsDelta({ removedDocuments: [uri] }); let textModelDisposableStore = this._editorEventListenersMapping.get(uri.toString()); textModelDisposableStore?.dispose(); this._editorEventListenersMapping.delete(URI.from(uri).toString()); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 0ad328d3549..1c7e49f5c97 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1633,7 +1633,7 @@ export interface ExtHostNotebookShape { $acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent): void; $acceptModelSaved(uriComponents: UriComponents): void; $acceptEditorPropertiesChanged(uriComponents: UriComponents, data: INotebookEditorPropertiesChangeData): void; - $acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta): Promise; + $acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta): void; $undoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise; $redoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index eeecd1409b8..ef20b264351 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -1513,7 +1513,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN this._editors.set(editorId, { editor }); } - async $acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta) { + $acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta): void { let editorChanged = false; if (delta.removedDocuments) { diff --git a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts index b1aaf836009..98908bbb0eb 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts @@ -10,7 +10,7 @@ import { TestRPCProtocol } from 'vs/workbench/test/browser/api/testRPCProtocol'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { NullLogService } from 'vs/platform/log/common/log'; import { mock } from 'vs/base/test/common/mock'; -import { MainContext, MainThreadCommandsShape, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; +import { IModelAddedData, MainContext, MainThreadCommandsShape, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostNotebookDocument, ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; import { CellKind, CellUri, NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { URI } from 'vs/base/common/uri'; @@ -47,7 +47,7 @@ suite('NotebookCell#Document', function () { let reg = extHostNotebooks.registerNotebookContentProvider(nullExtensionDescription, 'test', new class extends mock() { // async openNotebook() { } }); - await extHostNotebooks.$acceptDocumentAndEditorsDelta({ + extHostNotebooks.$acceptDocumentAndEditorsDelta({ addedDocuments: [{ handle: 0, uri: notebookUri, @@ -77,7 +77,7 @@ suite('NotebookCell#Document', function () { selections: [0] }] }); - await extHostNotebooks.$acceptDocumentAndEditorsDelta({ newActiveEditor: '_notebook_editor_0' }); + extHostNotebooks.$acceptDocumentAndEditorsDelta({ newActiveEditor: '_notebook_editor_0' }); notebook = extHostNotebooks.notebookDocuments[0]!; @@ -116,7 +116,7 @@ suite('NotebookCell#Document', function () { removedCellUris.push(doc.uri.toString()); }); - await extHostNotebooks.$acceptDocumentAndEditorsDelta({ removedDocuments: [notebook.uri] }); + extHostNotebooks.$acceptDocumentAndEditorsDelta({ removedDocuments: [notebook.uri] }); reg.dispose(); assert.strictEqual(removedCellUris.length, 2); @@ -174,4 +174,45 @@ suite('NotebookCell#Document', function () { await p; }); + + test('cell document stays open when notebook is still open', async function () { + + const docs: vscode.TextDocument[] = []; + const addData: IModelAddedData[] = []; + for (let cell of notebook.cells) { + const doc = extHostDocuments.getDocument(cell.uri); + assert.ok(doc); + assert.equal(extHostDocuments.getDocument(cell.uri).isClosed, false); + docs.push(doc); + addData.push({ + EOL: '\n', + isDirty: doc.isDirty, + lines: doc.getText().split('\n'), + modeId: doc.languageId, + uri: doc.uri, + versionId: doc.version + }); + } + + // this call happens when opening a document on the main side + extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ addedDocuments: addData }); + + // this call happens when closing a document from the main side + extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ removedDocuments: docs.map(d => d.uri) }); + + // notebook is still open -> cell documents stay open + for (let cell of notebook.cells) { + assert.ok(extHostDocuments.getDocument(cell.uri)); + assert.equal(extHostDocuments.getDocument(cell.uri).isClosed, false); + } + + // close notebook -> docs are closed + extHostNotebooks.$acceptDocumentAndEditorsDelta({ removedDocuments: [notebook.uri] }); + for (let cell of notebook.cells) { + assert.throws(() => extHostDocuments.getDocument(cell.uri)); + } + for (let doc of docs) { + assert.equal(doc.isClosed, true); + } + }); }); diff --git a/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts b/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts index 2349b4b6a99..237c1a30e4a 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts @@ -48,7 +48,7 @@ suite('NotebookConcatDocument', function () { let reg = extHostNotebooks.registerNotebookContentProvider(nullExtensionDescription, 'test', new class extends mock() { // async openNotebook() { } }); - await extHostNotebooks.$acceptDocumentAndEditorsDelta({ + extHostNotebooks.$acceptDocumentAndEditorsDelta({ addedDocuments: [{ handle: 0, uri: notebookUri, @@ -72,7 +72,7 @@ suite('NotebookConcatDocument', function () { } ] }); - await extHostNotebooks.$acceptDocumentAndEditorsDelta({ newActiveEditor: '_notebook_editor_0' }); + extHostNotebooks.$acceptDocumentAndEditorsDelta({ newActiveEditor: '_notebook_editor_0' }); notebook = extHostNotebooks.notebookDocuments[0]!; From c67a82da3aebe7cf002a493296d978bfc05546c3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 12 Aug 2020 09:45:39 +0200 Subject: [PATCH 135/736] throw TypeError when creating diagnostic without range or without message, fixes https://github.com/microsoft/vscode/issues/99410 --- src/vs/workbench/api/common/extHostTypes.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 9dc828a248f..4ba62a170c5 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -885,6 +885,12 @@ export class Diagnostic { tags?: DiagnosticTag[]; constructor(range: Range, message: string, severity: DiagnosticSeverity = DiagnosticSeverity.Error) { + if (!Range.isRange(range)) { + throw new TypeError('range must be set'); + } + if (!message) { + throw new TypeError('message must be set'); + } this.range = range; this.message = message; this.severity = severity; From 33b077a04048752ab5bfb8584e45f5fd73e139a0 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 12 Aug 2020 11:48:32 +0200 Subject: [PATCH 136/736] Fix json schema description for task quoting Fixes #104419 --- src/vs/workbench/contrib/tasks/common/jsonSchema_v2.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/common/jsonSchema_v2.ts b/src/vs/workbench/contrib/tasks/common/jsonSchema_v2.ts index 38ca7e709a8..f1afbaf7fdd 100644 --- a/src/vs/workbench/contrib/tasks/common/jsonSchema_v2.ts +++ b/src/vs/workbench/contrib/tasks/common/jsonSchema_v2.ts @@ -250,8 +250,8 @@ const command: IJSONSchema = { enum: ['escape', 'strong', 'weak'], enumDescriptions: [ nls.localize('JsonSchema.tasks.quoting.escape', 'Escapes characters using the shell\'s escape character (e.g. ` under PowerShell and \\ under bash).'), - nls.localize('JsonSchema.tasks.quoting.strong', 'Quotes the argument using the shell\'s strong quote character (e.g. " under PowerShell and bash).'), - nls.localize('JsonSchema.tasks.quoting.weak', 'Quotes the argument using the shell\'s weak quote character (e.g. \' under PowerShell and bash).'), + nls.localize('JsonSchema.tasks.quoting.strong', 'Quotes the argument using the shell\'s strong quote character (e.g. \' under PowerShell and bash).'), + nls.localize('JsonSchema.tasks.quoting.weak', 'Quotes the argument using the shell\'s weak quote character (e.g. " under PowerShell and bash).'), ], default: 'strong', description: nls.localize('JsonSchema.command.quotesString.quote', 'How the command value should be quoted.') @@ -283,8 +283,8 @@ const args: IJSONSchema = { enum: ['escape', 'strong', 'weak'], enumDescriptions: [ nls.localize('JsonSchema.tasks.quoting.escape', 'Escapes characters using the shell\'s escape character (e.g. ` under PowerShell and \\ under bash).'), - nls.localize('JsonSchema.tasks.quoting.strong', 'Quotes the argument using the shell\'s strong quote character (e.g. " under PowerShell and bash).'), - nls.localize('JsonSchema.tasks.quoting.weak', 'Quotes the argument using the shell\'s weak quote character (e.g. \' under PowerShell and bash).'), + nls.localize('JsonSchema.tasks.quoting.strong', 'Quotes the argument using the shell\'s strong quote character (e.g. \' under PowerShell and bash).'), + nls.localize('JsonSchema.tasks.quoting.weak', 'Quotes the argument using the shell\'s weak quote character (e.g. " under PowerShell and bash).'), ], default: 'strong', description: nls.localize('JsonSchema.args.quotesString.quote', 'How the argument value should be quoted.') From de7bb5b5fe55fe562361df6eeed61d970929c936 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 11 Aug 2020 16:23:57 +0200 Subject: [PATCH 137/736] tokenClassificationRegistry.ts: Use Object.create(null) --- src/vs/platform/theme/common/tokenClassificationRegistry.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/theme/common/tokenClassificationRegistry.ts b/src/vs/platform/theme/common/tokenClassificationRegistry.ts index e8bbd9403be..ab705dbcab3 100644 --- a/src/vs/platform/theme/common/tokenClassificationRegistry.ts +++ b/src/vs/platform/theme/common/tokenClassificationRegistry.ts @@ -312,8 +312,8 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { }; constructor() { - this.tokenTypeById = {}; - this.tokenModifierById = {}; + this.tokenTypeById = Object.create(null); + this.tokenModifierById = Object.create(null); this.typeHierarchy = Object.create(null); } From 4899592904f5f7e0231f98a863a30c6fdaf04660 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 12 Aug 2020 11:59:51 +0200 Subject: [PATCH 138/736] npm.fetchOnlinePackageInfo is not respected in latest build. Fixes #103540 --- .../npm/src/features/bowerJSONContribution.ts | 21 ++++++++++++------- .../npm/src/features/jsonContributions.ts | 2 +- .../src/features/packageJSONContribution.ts | 13 +++++++++--- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/extensions/npm/src/features/bowerJSONContribution.ts b/extensions/npm/src/features/bowerJSONContribution.ts index cd648732fc7..c3a827fd1e1 100644 --- a/extensions/npm/src/features/bowerJSONContribution.ts +++ b/extensions/npm/src/features/bowerJSONContribution.ts @@ -33,7 +33,7 @@ export class BowerJSONContribution implements IJSONContribution { return [{ language: 'json', scheme: '*', pattern: '**/bower.json' }, { language: 'json', scheme: '*', pattern: '**/.bower.json' }]; } - private onlineEnabled() { + private isEnabled() { return !!workspace.getConfiguration('npm').get('fetchOnlinePackageInfo'); } @@ -54,8 +54,11 @@ export class BowerJSONContribution implements IJSONContribution { } public collectPropertySuggestions(_resource: string, location: Location, currentWord: string, addValue: boolean, isLast: boolean, collector: ISuggestionsCollector): Thenable | null { + if (!this.isEnabled()) { + return null; + } if ((location.matches(['dependencies']) || location.matches(['devDependencies']))) { - if (currentWord.length > 0 && this.onlineEnabled()) { + if (currentWord.length > 0) { const queryUrl = 'https://registry.bower.io/packages/search/' + encodeURIComponent(currentWord); return this.xhr({ @@ -122,7 +125,10 @@ export class BowerJSONContribution implements IJSONContribution { return null; } - public collectValueSuggestions(_resource: string, location: Location, collector: ISuggestionsCollector): Thenable { + public collectValueSuggestions(_resource: string, location: Location, collector: ISuggestionsCollector): Promise | null { + if (!this.isEnabled()) { + return null; + } if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']))) { // not implemented. Could be do done calling the bower command. Waiting for web API: https://github.com/bower/registry/issues/26 const proposal = new CompletionItem(localize('json.bower.latest.version', 'latest')); @@ -132,7 +138,7 @@ export class BowerJSONContribution implements IJSONContribution { proposal.documentation = 'The latest version of the package'; collector.add(proposal); } - return Promise.resolve(null); + return null; } public resolveSuggestion(item: CompletionItem): Thenable | null { @@ -149,10 +155,6 @@ export class BowerJSONContribution implements IJSONContribution { } private getInfo(pack: string): Thenable { - if (!this.onlineEnabled()) { - return Promise.resolve(undefined); - } - const queryUrl = 'https://registry.bower.io/packages/' + encodeURIComponent(pack); return this.xhr({ @@ -181,6 +183,9 @@ export class BowerJSONContribution implements IJSONContribution { } public getInfoContribution(_resource: string, location: Location): Thenable | null { + if (!this.isEnabled()) { + return null; + } if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']))) { const pack = location.path[location.path.length - 1]; if (typeof pack === 'string') { diff --git a/extensions/npm/src/features/jsonContributions.ts b/extensions/npm/src/features/jsonContributions.ts index 3873b2dc31a..071d57b3348 100644 --- a/extensions/npm/src/features/jsonContributions.ts +++ b/extensions/npm/src/features/jsonContributions.ts @@ -25,7 +25,7 @@ export interface IJSONContribution { getDocumentSelector(): DocumentSelector; getInfoContribution(fileName: string, location: Location): Thenable | null; collectPropertySuggestions(fileName: string, location: Location, currentWord: string, addValue: boolean, isLast: boolean, result: ISuggestionsCollector): Thenable | null; - collectValueSuggestions(fileName: string, location: Location, result: ISuggestionsCollector): Thenable; + collectValueSuggestions(fileName: string, location: Location, result: ISuggestionsCollector): Thenable | null; collectDefaultSuggestions(fileName: string, result: ISuggestionsCollector): Thenable; resolveSuggestion?(item: CompletionItem): Thenable | null; } diff --git a/extensions/npm/src/features/packageJSONContribution.ts b/extensions/npm/src/features/packageJSONContribution.ts index 135b632071c..0e24b45aa28 100644 --- a/extensions/npm/src/features/packageJSONContribution.ts +++ b/extensions/npm/src/features/packageJSONContribution.ts @@ -51,6 +51,10 @@ export class PackageJSONContribution implements IJSONContribution { return Promise.resolve(null); } + private isEnabled() { + return this.canRunNPM || this.onlineEnabled(); + } + private onlineEnabled() { return !!workspace.getConfiguration('npm').get('fetchOnlinePackageInfo'); } @@ -63,7 +67,7 @@ export class PackageJSONContribution implements IJSONContribution { isLast: boolean, collector: ISuggestionsCollector ): Thenable | null { - if (!this.onlineEnabled()) { + if (!this.isEnabled()) { return null; } @@ -180,7 +184,7 @@ export class PackageJSONContribution implements IJSONContribution { } public async collectValueSuggestions(_fileName: string, location: Location, result: ISuggestionsCollector): Promise { - if (!this.onlineEnabled()) { + if (!this.isEnabled()) { return null; } @@ -250,7 +254,7 @@ export class PackageJSONContribution implements IJSONContribution { if (this.canRunNPM) { info = await this.npmView(pack); } - if (!info) { + if (!info && this.onlineEnabled()) { info = await this.npmjsView(pack); } return info; @@ -303,6 +307,9 @@ export class PackageJSONContribution implements IJSONContribution { } public getInfoContribution(_fileName: string, location: Location): Thenable | null { + if (!this.isEnabled()) { + return null; + } if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']) || location.matches(['optionalDependencies', '*']) || location.matches(['peerDependencies', '*']))) { const pack = location.path[location.path.length - 1]; if (typeof pack === 'string') { From 52e38713f25178333522bd7295e76ecb84170465 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 12 Aug 2020 14:46:04 +0200 Subject: [PATCH 139/736] Allow workspace tasks to be executed through API Fixes #104408 --- .../workbench/api/browser/mainThreadTask.ts | 32 ++++++++++++++----- src/vs/workbench/api/common/extHostTask.ts | 8 +++-- src/vs/workbench/api/node/extHostTask.ts | 4 +-- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTask.ts b/src/vs/workbench/api/browser/mainThreadTask.ts index 536fd02dbd3..586acc3ab8d 100644 --- a/src/vs/workbench/api/browser/mainThreadTask.ts +++ b/src/vs/workbench/api/browser/mainThreadTask.ts @@ -5,14 +5,14 @@ import * as nls from 'vs/nls'; -import { URI } from 'vs/base/common/uri'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import * as Types from 'vs/base/common/types'; import * as Platform from 'vs/base/common/platform'; import { IStringDictionary, forEach } from 'vs/base/common/collections'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ContributedTask, ConfiguringTask, KeyedTaskIdentifier, TaskExecution, Task, TaskEvent, TaskEventKind, @@ -509,11 +509,27 @@ export class MainThreadTask implements MainThreadTaskShape { }); } + private getWorkspace(value: UriComponents | string): string | IWorkspace | IWorkspaceFolder | null { + let workspace; + if (typeof value === 'string') { + workspace = value; + } else { + const workspaceObject = this._workspaceContextServer.getWorkspace(); + const uri = URI.revive(value); + if (workspaceObject.configuration?.toString() === uri.toString()) { + workspace = workspaceObject; + } else { + workspace = this._workspaceContextServer.getWorkspaceFolder(uri); + } + } + return workspace; + } + public async $getTaskExecution(value: TaskHandleDTO | TaskDTO): Promise { if (TaskHandleDTO.is(value)) { - const workspaceFolder = typeof value.workspaceFolder === 'string' ? value.workspaceFolder : this._workspaceContextServer.getWorkspaceFolder(URI.revive(value.workspaceFolder)); - if (workspaceFolder) { - const task = await this._taskService.getTask(workspaceFolder, value.id, true); + const workspace = this.getWorkspace(value.workspaceFolder); + if (workspace) { + const task = await this._taskService.getTask(workspace, value.id, true); if (task) { return { id: task._id, @@ -538,9 +554,9 @@ export class MainThreadTask implements MainThreadTaskShape { public $executeTask(value: TaskHandleDTO | TaskDTO): Promise { return new Promise((resolve, reject) => { if (TaskHandleDTO.is(value)) { - const workspaceFolder = typeof value.workspaceFolder === 'string' ? value.workspaceFolder : this._workspaceContextServer.getWorkspaceFolder(URI.revive(value.workspaceFolder)); - if (workspaceFolder) { - this._taskService.getTask(workspaceFolder, value.id, true).then((task: Task | undefined) => { + const workspace = this.getWorkspace(value.workspaceFolder); + if (workspace) { + this._taskService.getTask(workspace, value.id, true).then((task: Task | undefined) => { if (!task) { reject(new Error('Task not found')); } else { diff --git a/src/vs/workbench/api/common/extHostTask.ts b/src/vs/workbench/api/common/extHostTask.ts index ee6423408fd..610e10ec65e 100644 --- a/src/vs/workbench/api/common/extHostTask.ts +++ b/src/vs/workbench/api/common/extHostTask.ts @@ -192,12 +192,16 @@ export namespace CustomExecutionDTO { export namespace TaskHandleDTO { - export function from(value: types.Task): tasks.TaskHandleDTO { + export function from(value: types.Task, workspaceService?: IExtHostWorkspace): tasks.TaskHandleDTO { let folder: UriComponents | string; if (value.scope !== undefined && typeof value.scope !== 'number') { folder = value.scope.uri; } else if (value.scope !== undefined && typeof value.scope === 'number') { - folder = USER_TASKS_GROUP_KEY; + if ((value.scope === types.TaskScope.Workspace) && workspaceService && workspaceService.workspaceFile) { + folder = workspaceService.workspaceFile; + } else { + folder = USER_TASKS_GROUP_KEY; + } } return { id: value._id!, diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index 0c59bbd7a6f..311a85fa2b2 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -32,7 +32,7 @@ export class ExtHostTask extends ExtHostTaskBase { constructor( @IExtHostRpcService extHostRpc: IExtHostRpcService, @IExtHostInitDataService initData: IExtHostInitDataService, - @IExtHostWorkspace workspaceService: IExtHostWorkspace, + @IExtHostWorkspace private readonly workspaceService: IExtHostWorkspace, @IExtHostDocumentsAndEditors editorService: IExtHostDocumentsAndEditors, @IExtHostConfiguration configurationService: IExtHostConfiguration, @IExtHostTerminalService extHostTerminalService: IExtHostTerminalService, @@ -55,7 +55,7 @@ export class ExtHostTask extends ExtHostTaskBase { // We have a preserved ID. So the task didn't change. if (tTask._id !== undefined) { // Always get the task execution first to prevent timing issues when retrieving it later - const handleDto = TaskHandleDTO.from(tTask); + const handleDto = TaskHandleDTO.from(tTask, this.workspaceService); const executionDTO = await this._proxy.$getTaskExecution(handleDto); if (executionDTO.task === undefined) { throw new Error('Task from execution DTO is undefined'); From 263188f08b4692b359428739a0187ef7a23ca500 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 12 Aug 2020 14:50:15 +0200 Subject: [PATCH 140/736] memento - add error logging for #102251 --- src/vs/workbench/common/memento.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/common/memento.ts b/src/vs/workbench/common/memento.ts index 97b12facfd9..309b0517b56 100644 --- a/src/vs/workbench/common/memento.ts +++ b/src/vs/workbench/common/memento.ts @@ -5,6 +5,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { isEmptyObject } from 'vs/base/common/types'; +import { onUnexpectedError } from 'vs/base/common/errors'; export type MementoObject = { [key: string]: any }; @@ -75,7 +76,15 @@ class ScopedMemento { private load(): MementoObject { const memento = this.storageService.get(this.id, this.scope); if (memento) { - return JSON.parse(memento); + try { + return JSON.parse(memento); + } catch (error) { + // Seeing reports from users unable to open editors + // from memento parsing exceptions. Log the contents + // to diagnose further + // https://github.com/microsoft/vscode/issues/102251 + onUnexpectedError(`[memento]: failed to parse contents: ${error} (id: ${this.id}, scope: ${this.scope}, contents: ${memento})`); + } } return {}; From ef569d3b9aa3d7ccb0afabd370565d97ffb961f5 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 12 Aug 2020 14:59:05 +0200 Subject: [PATCH 141/736] fixes #104475 --- src/vs/workbench/contrib/debug/browser/callStackView.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 2642d372de5..1d4f3bf717d 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -161,7 +161,6 @@ export class CallStackView extends ViewPane { dom.toggleClass(this.pauseMessageLabel, 'exception', thread.stoppedDetails.reason === 'exception'); this.pauseMessage.hidden = false; this.updateActions(); - } else { this.pauseMessage.hidden = true; this.updateActions(); @@ -550,6 +549,9 @@ class SessionsRenderer implements ICompressibleTreeRenderer s.parentSession === session); if (!hasChildSessions) { From e3dc5a339c6a7d8bfe99f56cc807ec24db6d550a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 12 Aug 2020 15:56:41 +0200 Subject: [PATCH 142/736] Startup error dialog closes immediately (fix #104493) --- src/vs/code/electron-main/main.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 757602c8672..6eff9997fbe 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -289,7 +289,8 @@ class CodeMain { // Process Info if (args.status) { - return instantiationService.invokeFunction(async accessor => { + return instantiationService.invokeFunction(async () => { + // Create a diagnostic service connected to the existing shared process const sharedProcessClient = await connect(environmentService.sharedIPCHandle, 'main'); const diagnosticsChannel = sharedProcessClient.getChannel('diagnostics'); @@ -357,7 +358,10 @@ class CodeMain { } private showStartupWarningDialog(message: string, detail: string): void { - dialog.showMessageBox({ + // use sync variant here because we likely exit after this method + // due to startup issues and otherwise the dialog seems to disappear + // https://github.com/microsoft/vscode/issues/104493 + dialog.showMessageBoxSync({ title: product.nameLong, type: 'warning', buttons: [mnemonicButtonLabel(localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close"))], From 0da75b92c032ba6a40a4527c6bdb25c01ecf8cf9 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 12 Aug 2020 15:51:08 +0200 Subject: [PATCH 143/736] notebooks - use ResourceMap --- .../workbench/api/common/extHostNotebook.ts | 142 +++++++++--------- 1 file changed, 70 insertions(+), 72 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index ef20b264351..78bed2eecca 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -25,6 +25,7 @@ import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/we import { CellEditType, CellOutputKind, diff, ICellDeleteEdit, ICellEditOperation, ICellInsertEdit, IMainCellDto, INotebookDisplayOrder, INotebookEditData, INotebookKernelInfoDto2, IOutputRenderRequest, IOutputRenderResponse, IOutputRenderResponseCellInfo, IOutputRenderResponseOutputInfo, IProcessedOutput, IRawOutput, NotebookCellMetadata, NotebookCellsChangedEvent, NotebookCellsChangeType, NotebookCellsSplice2, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import * as vscode from 'vscode'; import { Cache } from './cache'; +import { ResourceMap } from 'vs/base/common/map'; interface IObservable { @@ -881,8 +882,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN private readonly _notebookContentProviders = new Map(); private readonly _notebookKernels = new Map(); private readonly _notebookKernelProviders = new Map(); - private readonly _documents = new Map(); - private readonly _unInitializedDocuments = new Map(); + private readonly _documents = new ResourceMap(); + private readonly _unInitializedDocuments = new ResourceMap(); private readonly _editors = new Map(); private readonly _webviewComm = new Map(); private readonly _notebookOutputRenderers = new Map(); @@ -980,7 +981,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN throw new Error(`Notebook renderer for '${id}' is not registered`); } - const document = this._documents.get(URI.revive(uriComponents).toString()); + const document = this._documents.get(URI.revive(uriComponents)); if (!document) { return; @@ -1018,7 +1019,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN throw new Error(`Notebook renderer for '${id}' is not registered`); } - const document = this._documents.get(URI.revive(uriComponents).toString()); + const document = this._documents.get(URI.revive(uriComponents)); if (!document) { return; @@ -1076,7 +1077,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN const listener = provider.onDidChangeNotebook ? provider.onDidChangeNotebook(e => { - const document = this._documents.get(URI.revive(e.document.uri).toString()); + const document = this._documents.get(URI.revive(e.document.uri)); if (!document) { throw new Error(`Notebook document ${e.document.uri.toString()} not found`); @@ -1120,7 +1121,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } private _withAdapter(handle: number, uri: UriComponents, callback: (adapter: ExtHostNotebookKernelProviderAdapter, document: ExtHostNotebookDocument) => Promise) { - const document = this._documents.get(URI.revive(uri).toString()); + const document = this._documents.get(URI.revive(uri)); if (!document) { return []; @@ -1169,57 +1170,56 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN async $resolveNotebookData(viewType: string, uri: UriComponents, backupId?: string): Promise { const provider = this._notebookContentProviders.get(viewType); const revivedUri = URI.revive(uri); - - if (provider) { - let storageRoot: URI | undefined; - if (this._extensionStoragePaths) { - storageRoot = this._extensionStoragePaths.workspaceValue(provider.extension) ?? this._extensionStoragePaths.globalValue(provider.extension); - } - - let document = this._documents.get(URI.revive(uri).toString()); - - if (!document) { - const that = this; - document = this._unInitializedDocuments.get(revivedUri.toString()) ?? new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, { - emitModelChange(event: vscode.NotebookCellsChangeEvent): void { - that._onDidChangeNotebookCells.fire(event); - }, - emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void { - that._onDidChangeCellOutputs.fire(event); - }, - emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void { - that._onDidChangeCellLanguage.fire(event); - }, - emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void { - that._onDidChangeCellMetadata.fire(event); - }, - }, viewType, revivedUri, this, storageRoot); - this._unInitializedDocuments.set(revivedUri.toString(), document); - } - - const rawCells = await provider.provider.openNotebook(URI.revive(uri), { backupId }); - const dto = { - metadata: { - ...notebookDocumentMetadataDefaults, - ...rawCells.metadata - }, - languages: rawCells.languages, - cells: rawCells.cells.map(cell => ({ - ...cell, - outputs: cell.outputs.map(o => addIdToOutput(o)) - })), - }; - - return dto; + if (!provider) { + return; } - return; + let storageRoot: URI | undefined; + if (this._extensionStoragePaths) { + storageRoot = this._extensionStoragePaths.workspaceValue(provider.extension) ?? this._extensionStoragePaths.globalValue(provider.extension); + } + + let document = this._documents.get(revivedUri); + + if (!document) { + const that = this; + document = this._unInitializedDocuments.get(revivedUri) ?? new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, { + emitModelChange(event: vscode.NotebookCellsChangeEvent): void { + that._onDidChangeNotebookCells.fire(event); + }, + emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void { + that._onDidChangeCellOutputs.fire(event); + }, + emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void { + that._onDidChangeCellLanguage.fire(event); + }, + emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void { + that._onDidChangeCellMetadata.fire(event); + }, + }, viewType, revivedUri, this, storageRoot); + this._unInitializedDocuments.set(revivedUri, document); + } + + const rawCells = await provider.provider.openNotebook(URI.revive(uri), { backupId }); + const dto = { + metadata: { + ...notebookDocumentMetadataDefaults, + ...rawCells.metadata + }, + languages: rawCells.languages, + cells: rawCells.cells.map(cell => ({ + ...cell, + outputs: cell.outputs.map(o => addIdToOutput(o)) + })), + }; + + return dto; } async $resolveNotebookEditor(viewType: string, uri: UriComponents, editorId: string): Promise { const provider = this._notebookContentProviders.get(viewType); const revivedUri = URI.revive(uri); - const document = this._documents.get(revivedUri.toString()); + const document = this._documents.get(revivedUri); if (!document || !provider) { return; } @@ -1258,7 +1258,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } async $executeNotebookByAttachedKernel(viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise { - const document = this._documents.get(URI.revive(uri).toString()); + const document = this._documents.get(URI.revive(uri)); if (!document) { return; @@ -1279,7 +1279,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } async $cancelNotebookByAttachedKernel(viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise { - const document = this._documents.get(URI.revive(uri).toString()); + const document = this._documents.get(URI.revive(uri)); if (!document) { return; @@ -1316,7 +1316,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } async $executeNotebook2(kernelId: string, viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise { - const document = this._documents.get(URI.revive(uri).toString()); + const document = this._documents.get(URI.revive(uri)); if (!document || document.viewType !== viewType) { return; @@ -1338,7 +1338,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } async $saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise { - const document = this._documents.get(URI.revive(uri).toString()); + const document = this._documents.get(URI.revive(uri)); if (!document) { return false; } @@ -1352,7 +1352,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } async $saveNotebookAs(viewType: string, uri: UriComponents, target: UriComponents, token: CancellationToken): Promise { - const document = this._documents.get(URI.revive(uri).toString()); + const document = this._documents.get(URI.revive(uri)); if (!document) { return false; } @@ -1366,7 +1366,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } async $undoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise { - const document = this._documents.get(URI.revive(uri).toString()); + const document = this._documents.get(URI.revive(uri)); if (!document) { return; } @@ -1376,7 +1376,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } async $redoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise { - const document = this._documents.get(URI.revive(uri).toString()); + const document = this._documents.get(URI.revive(uri)); if (!document) { return; } @@ -1386,7 +1386,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN async $backup(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise { - const document = this._documents.get(URI.revive(uri).toString()); + const document = this._documents.get(URI.revive(uri)); const provider = this._notebookContentProviders.get(viewType); if (document && provider && provider.provider.backupNotebook) { @@ -1435,7 +1435,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN $acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent): void { - const document = this._documents.get(URI.revive(uriComponents).toString()); + const document = this._documents.get(URI.revive(uriComponents)); if (document) { document.acceptModelChanged(event); @@ -1443,7 +1443,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } public $acceptModelSaved(uriComponents: UriComponents): void { - const document = this._documents.get(URI.revive(uriComponents).toString()); + const document = this._documents.get(URI.revive(uriComponents)); if (document) { // this.$acceptDirtyStateChanged(uriComponents, false); this._onDidSaveNotebookDocument.fire(document); @@ -1519,18 +1519,17 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN if (delta.removedDocuments) { delta.removedDocuments.forEach((uri) => { const revivedUri = URI.revive(uri); - const revivedUriStr = revivedUri.toString(); - const document = this._documents.get(revivedUriStr); + const document = this._documents.get(revivedUri); if (document) { document.dispose(); - this._documents.delete(revivedUriStr); + this._documents.delete(revivedUri); this._documentsAndEditors.$acceptDocumentsAndEditorsDelta({ removedDocuments: document.cells.map(cell => cell.uri) }); this._onDidCloseNotebookDocument.fire(document); } [...this._editors.values()].forEach((e) => { - if (e.editor.uri.toString() === revivedUriStr) { + if (e.editor.uri.toString() === revivedUri.toString()) { e.editor.dispose(); this._editors.delete(e.editor.id); editorChanged = true; @@ -1545,7 +1544,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN delta.addedDocuments.forEach(modelData => { const revivedUri = URI.revive(modelData.uri); - const revivedUriStr = revivedUri.toString(); const viewType = modelData.viewType; const entry = this._notebookContentProviders.get(viewType); let storageRoot: URI | undefined; @@ -1553,10 +1551,10 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN storageRoot = this._extensionStoragePaths.workspaceValue(entry.extension) ?? this._extensionStoragePaths.globalValue(entry.extension); } - if (!this._documents.has(revivedUriStr)) { + if (!this._documents.has(revivedUri)) { const that = this; - const document = this._unInitializedDocuments.get(revivedUriStr) ?? new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, { + const document = this._unInitializedDocuments.get(revivedUri) ?? new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, { emitModelChange(event: vscode.NotebookCellsChangeEvent): void { that._onDidChangeNotebookCells.fire(event); }, @@ -1571,7 +1569,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } }, viewType, revivedUri, this, storageRoot); - this._unInitializedDocuments.delete(revivedUriStr); + this._unInitializedDocuments.delete(revivedUri); if (modelData.metadata) { document.metadata = { ...notebookDocumentMetadataDefaults, @@ -1592,8 +1590,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN // add cell document as vscode.TextDocument addedCellDocuments.push(...modelData.cells.map(ExtHostCell.asModelAddData)); - this._documents.get(revivedUriStr)?.dispose(); - this._documents.set(revivedUriStr, document); + this._documents.get(revivedUri)?.dispose(); + this._documents.set(revivedUri, document); // create editor if populated if (modelData.attachedEditor) { @@ -1606,7 +1604,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN addedDocuments: addedCellDocuments }); - const document = this._documents.get(revivedUriStr)!; + const document = this._documents.get(revivedUri)!; this._onDidOpenNotebookDocument.fire(document); }); } @@ -1618,7 +1616,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } const revivedUri = URI.revive(editorModelData.documentUri); - const document = this._documents.get(revivedUri.toString()); + const document = this._documents.get(revivedUri); if (document) { this._createExtHostEditor(document, editorModelData.id, editorModelData.selections); From 26605048eabbd941a8a069df13a53a064b3ff0ac Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 12 Aug 2020 15:58:53 +0200 Subject: [PATCH 144/736] :lipstick: forof over foreach --- .../workbench/api/common/extHostNotebook.ts | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 78bed2eecca..82fbb048e75 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -1517,7 +1517,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN let editorChanged = false; if (delta.removedDocuments) { - delta.removedDocuments.forEach((uri) => { + for (const uri of delta.removedDocuments) { const revivedUri = URI.revive(uri); const document = this._documents.get(revivedUri); @@ -1528,21 +1528,21 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN this._onDidCloseNotebookDocument.fire(document); } - [...this._editors.values()].forEach((e) => { + for (const e of this._editors.values()) { if (e.editor.uri.toString() === revivedUri.toString()) { e.editor.dispose(); this._editors.delete(e.editor.id); editorChanged = true; } - }); - }); + } + } } if (delta.addedDocuments) { const addedCellDocuments: IModelAddedData[] = []; - delta.addedDocuments.forEach(modelData => { + for (const modelData of delta.addedDocuments) { const revivedUri = URI.revive(modelData.uri); const viewType = modelData.viewType; const entry = this._notebookContentProviders.get(viewType); @@ -1606,11 +1606,11 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN const document = this._documents.get(revivedUri)!; this._onDidOpenNotebookDocument.fire(document); - }); + } } if (delta.addedEditors) { - delta.addedEditors.forEach(editorModelData => { + for (const editorModelData of delta.addedEditors) { if (this._editors.has(editorModelData.id)) { return; } @@ -1622,13 +1622,13 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN this._createExtHostEditor(document, editorModelData.id, editorModelData.selections); editorChanged = true; } - }); + } } const removedEditors: { editor: ExtHostNotebookEditor; }[] = []; if (delta.removedEditors) { - delta.removedEditors.forEach(editorid => { + for (const editorid of delta.removedEditors) { const editor = this._editors.get(editorid); if (editor) { @@ -1641,7 +1641,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN removedEditors.push(editor); } - }); + } } if (editorChanged) { @@ -1655,10 +1655,10 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN const visibleEditorsSet = new Set(); this.visibleNotebookEditors.forEach(editor => visibleEditorsSet.add(editor.id)); - [...this._editors.values()].forEach((e) => { + for (const e of this._editors.values()) { const newValue = visibleEditorsSet.has(e.editor.id); e.editor._acceptVisibility(newValue); - }); + } this.visibleNotebookEditors = [...this._editors.values()].map(e => e.editor).filter(e => e.visible); this._onDidChangeVisibleNotebookEditors.fire(this.visibleNotebookEditors); @@ -1668,19 +1668,17 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN if (delta.newActiveEditor) { this._activeNotebookEditor = this._editors.get(delta.newActiveEditor)?.editor; this._activeNotebookEditor?._acceptActive(true); - [...this._editors.values()].forEach((e) => { + for (const e of this._editors.values()) { if (e.editor !== this.activeNotebookEditor) { e.editor._acceptActive(false); } - }); + } } else { // clear active notebook as current active editor is non-notebook editor this._activeNotebookEditor = undefined; - - [...this._editors.values()].forEach((e) => { + for (const e of this._editors.values()) { e.editor._acceptActive(false); - }); - + } } this._onDidChangeActiveNotebookEditor.fire(this._activeNotebookEditor); From af3c1cfdef66271159d8ad54adaf5f067810065c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 12 Aug 2020 16:19:47 +0200 Subject: [PATCH 145/736] maybe maybe for https://github.com/microsoft/vscode/issues/104485 --- src/vs/editor/contrib/suggest/test/suggestModel.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/editor/contrib/suggest/test/suggestModel.test.ts b/src/vs/editor/contrib/suggest/test/suggestModel.test.ts index 360ce3a79d1..7373a0db710 100644 --- a/src/vs/editor/contrib/suggest/test/suggestModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/suggestModel.test.ts @@ -35,12 +35,15 @@ import { MockKeybindingService } from 'vs/platform/keybinding/test/common/mockKe import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { mock } from 'vs/base/test/common/mock'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; function createMockEditor(model: TextModel): ITestCodeEditor { let editor = createTestCodeEditor({ model: model, serviceCollection: new ServiceCollection( + [IConfigurationService, TestConfigurationService], [ITelemetryService, NullTelemetryService], [IStorageService, new InMemoryStorageService()], [IKeybindingService, new MockKeybindingService()], From 1b0d4db60ccd546680b06703e8c53f6c6165c2d1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 12 Aug 2020 16:26:49 +0200 Subject: [PATCH 146/736] use $-prefix for IPC functions only --- .../workbench/api/common/extHostNotebook.ts | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 82fbb048e75..8c5266dc2d8 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -341,23 +341,23 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo acceptModelChanged(event: NotebookCellsChangedEvent): void { this._versionId = event.versionId; if (event.kind === NotebookCellsChangeType.Initialize) { - this.$spliceNotebookCells(event.changes, true); + this._spliceNotebookCells(event.changes, true); } if (event.kind === NotebookCellsChangeType.ModelChange) { - this.$spliceNotebookCells(event.changes, false); + this._spliceNotebookCells(event.changes, false); } else if (event.kind === NotebookCellsChangeType.Move) { - this.$moveCell(event.index, event.newIdx); + this._moveCell(event.index, event.newIdx); } else if (event.kind === NotebookCellsChangeType.CellClearOutput) { - this.$clearCellOutputs(event.index); + this._clearCellOutputs(event.index); } else if (event.kind === NotebookCellsChangeType.CellsClearOutput) { - this.$clearAllCellOutputs(); + this._clearAllCellOutputs(); } else if (event.kind === NotebookCellsChangeType.ChangeLanguage) { - this.$changeCellLanguage(event.index, event.language); + this._changeCellLanguage(event.index, event.language); } else if (event.kind === NotebookCellsChangeType.ChangeMetadata) { - this.$changeCellMetadata(event.index, event.metadata); + this._changeCellMetadata(event.index, event.metadata); } } - private $spliceNotebookCells(splices: NotebookCellsSplice2[], initialization: boolean): void { + private _spliceNotebookCells(splices: NotebookCellsSplice2[], initialization: boolean): void { if (this._disposed) { return; } @@ -415,7 +415,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo } } - private $moveCell(index: number, newIdx: number): void { + private _moveCell(index: number, newIdx: number): void { const cells = this.cells.splice(index, 1); this.cells.splice(newIdx, 0, ...cells); const changes: vscode.NotebookCellsChangeData[] = [{ @@ -435,14 +435,14 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo }); } - private $clearCellOutputs(index: number): void { + private _clearCellOutputs(index: number): void { const cell = this.cells[index]; cell.outputs = []; const event: vscode.NotebookCellOutputsChangeEvent = { document: this, cells: [cell] }; this._emitter.emitCellOutputsChange(event); } - private $clearAllCellOutputs(): void { + private _clearAllCellOutputs(): void { const modifedCells: vscode.NotebookCell[] = []; this.cells.forEach(cell => { if (cell.outputs.length !== 0) { @@ -454,13 +454,13 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo this._emitter.emitCellOutputsChange(event); } - private $changeCellLanguage(index: number, language: string): void { + private _changeCellLanguage(index: number, language: string): void { const cell = this.cells[index]; const event: vscode.NotebookCellLanguageChangeEvent = { document: this, cell, language }; this._emitter.emitCellLanguageChange(event); } - private $changeCellMetadata(index: number, newMetadata: NotebookCellMetadata): void { + private _changeCellMetadata(index: number, newMetadata: NotebookCellMetadata): void { const cell = this.cells[index]; cell.setMetadata(newMetadata); const event: vscode.NotebookCellMetadataChangeEvent = { document: this, cell }; From e3a0f6a9e309b342cbcbb304b34f3c4ba27fa186 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 12 Aug 2020 16:48:08 +0200 Subject: [PATCH 147/736] debug: use cancellation tokens for stackTrace aslo fixes #103611 --- .../contrib/debug/browser/debugSession.ts | 15 ++++++++------- src/vs/workbench/contrib/debug/browser/repl.ts | 2 +- src/vs/workbench/contrib/debug/common/debug.ts | 4 ++-- .../workbench/contrib/debug/common/debugModel.ts | 10 ++++++++-- .../contrib/debug/test/common/mockDebug.ts | 5 +++-- 5 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index cd67a54853f..19c471f74bd 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -448,13 +448,13 @@ export class DebugSession implements IDebugSession { return this.raw.custom(request, args); } - stackTrace(threadId: number, startFrame: number, levels: number): Promise { + stackTrace(threadId: number, startFrame: number, levels: number, token: CancellationToken): Promise { if (!this.raw) { throw new Error(localize('noDebugAdapter', "No debug adapter, can not send '{0}'", 'stackTrace')); } - const token = this.getNewCancellationToken(threadId); - return this.raw.stackTrace({ threadId, startFrame, levels }, token); + const sessionToken = this.getNewCancellationToken(threadId, token); + return this.raw.stackTrace({ threadId, startFrame, levels }, sessionToken); } async exceptionInfo(threadId: number): Promise { @@ -628,17 +628,18 @@ export class DebugSession implements IDebugSession { } } - async completions(frameId: number | undefined, text: string, position: Position, overwriteBefore: number, token: CancellationToken): Promise { + async completions(frameId: number | undefined, threadId: number, text: string, position: Position, overwriteBefore: number, token: CancellationToken): Promise { if (!this.raw) { return Promise.reject(new Error(localize('noDebugAdapter', "No debug adapter, can not send '{0}'", 'completions'))); } + const sessionCancelationToken = this.getNewCancellationToken(threadId, token); return this.raw.completions({ frameId, text, column: position.column, line: position.lineNumber, - }, token); + }, sessionCancelationToken); } async stepInTargets(frameId: number): Promise<{ id: number, label: string }[]> { @@ -1053,8 +1054,8 @@ export class DebugSession implements IDebugSession { } } - private getNewCancellationToken(threadId: number): CancellationToken { - const tokenSource = new CancellationTokenSource(); + private getNewCancellationToken(threadId: number, token?: CancellationToken): CancellationToken { + const tokenSource = new CancellationTokenSource(token); const tokens = this.cancellationMap.get(threadId) || []; tokens.push(tokenSource); this.cancellationMap.set(threadId, tokens); diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 6afa8ef0e4f..a5e01950a9a 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -141,7 +141,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { const text = model.getValue(); const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame; const frameId = focusedStackFrame ? focusedStackFrame.frameId : undefined; - const response = await session.completions(frameId, text, position, overwriteBefore, token); + const response = await session.completions(frameId, focusedStackFrame?.thread.threadId || 0, text, position, overwriteBefore, token); const suggestions: CompletionItem[] = []; const computeRange = (length: number) => Range.fromPositions(position.delta(0, -length), position); diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index d74d8cce52f..163c77279e3 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -225,7 +225,7 @@ export interface IDebugSession extends ITreeElement { sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise; breakpointsLocations(uri: uri, lineNumber: number): Promise; - stackTrace(threadId: number, startFrame: number, levels: number): Promise; + stackTrace(threadId: number, startFrame: number, levels: number, token: CancellationToken): Promise; exceptionInfo(threadId: number): Promise; scopes(frameId: number, threadId: number): Promise; variables(variablesReference: number, threadId: number | undefined, filter: 'indexed' | 'named' | undefined, start: number | undefined, count: number | undefined): Promise; @@ -244,7 +244,7 @@ export interface IDebugSession extends ITreeElement { terminateThreads(threadIds: number[]): Promise; stepInTargets(frameId: number): Promise<{ id: number, label: string }[]>; - completions(frameId: number | undefined, text: string, position: Position, overwriteBefore: number, token: CancellationToken): Promise; + completions(frameId: number | undefined, threadId: number, text: string, position: Position, overwriteBefore: number, token: CancellationToken): Promise; setVariable(variablesReference: number | undefined, name: string, value: string): Promise; loadSource(resource: uri): Promise; getLoadedSources(): Promise; diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 1a8a5879ca6..f45a693bbc7 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -22,6 +22,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { ITextEditorPane } from 'vs/workbench/common/editor'; import { mixin } from 'vs/base/common/objects'; import { DebugStorage } from 'vs/workbench/contrib/debug/common/debugStorage'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; export class ExpressionContainer implements IExpressionContainer { @@ -366,6 +367,7 @@ export class StackFrame implements IStackFrame { export class Thread implements IThread { private callStack: IStackFrame[]; private staleCallStack: IStackFrame[]; + private callStackCancellationTokens: CancellationTokenSource[] = []; public stoppedDetails: IRawStoppedDetails | undefined; public stopped: boolean; @@ -384,6 +386,8 @@ export class Thread implements IThread { this.staleCallStack = this.callStack; } this.callStack = []; + this.callStackCancellationTokens.forEach(c => c.dispose(true)); + this.callStackCancellationTokens = []; } getCallStack(): IStackFrame[] { @@ -424,8 +428,10 @@ export class Thread implements IThread { private async getCallStackImpl(startFrame: number, levels: number): Promise { try { - const response = await this.session.stackTrace(this.threadId, startFrame, levels); - if (!response || !response.body) { + const tokenSource = new CancellationTokenSource(); + this.callStackCancellationTokens.push(tokenSource); + const response = await this.session.stackTrace(this.threadId, startFrame, levels, tokenSource.token); + if (!response || !response.body || tokenSource.token.isCancellationRequested) { return []; } diff --git a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts index 7e9b4377b82..f3057da0e95 100644 --- a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts +++ b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts @@ -14,6 +14,7 @@ import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstract import { DebugStorage } from 'vs/workbench/contrib/debug/common/debugStorage'; import { ExceptionBreakpoint, Expression, DataBreakpoint, FunctionBreakpoint, Breakpoint, DebugModel } from 'vs/workbench/contrib/debug/common/debugModel'; import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompoundRoot'; +import { CancellationToken } from 'vs/base/common/cancellation'; export class MockDebugService implements IDebugService { @@ -266,7 +267,7 @@ export class MockSession implements IDebugSession { return Promise.resolve([]); } - completions(frameId: number, text: string, position: Position, overwriteBefore: number): Promise { + completions(frameId: number, threadId: number, text: string, position: Position, overwriteBefore: number): Promise { throw new Error('not implemented'); } @@ -295,7 +296,7 @@ export class MockSession implements IDebugSession { customRequest(request: string, args: any): Promise { throw new Error('Method not implemented.'); } - stackTrace(threadId: number, startFrame: number, levels: number): Promise { + stackTrace(threadId: number, startFrame: number, levels: number, token: CancellationToken): Promise { throw new Error('Method not implemented.'); } exceptionInfo(threadId: number): Promise { From c6c9d5b41f79c3777d1bc037070f347fa1ea8a58 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 12 Aug 2020 17:00:59 +0200 Subject: [PATCH 148/736] debug: more precise computatino of top stack frame call stack decoration --- .../contrib/debug/browser/callStackEditorContribution.ts | 4 ++-- src/vs/workbench/contrib/debug/browser/debugService.ts | 9 +++------ src/vs/workbench/contrib/debug/common/debug.ts | 6 ++++++ src/vs/workbench/contrib/debug/common/debugModel.ts | 6 +++++- .../contrib/debug/test/browser/callStack.test.ts | 4 ++-- 5 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts index bc1cac85a06..023fa7edd57 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts @@ -48,8 +48,8 @@ export function createDecorationsForStackFrame(stackFrame: IStackFrame, topStack // compute how to decorate the editor. Different decorations are used if this is a top stack frame, focused stack frame, // an exception or a stack frame that did not change the line number (we only decorate the columns, not the whole line). - const callStack = stackFrame.thread.getCallStack(); - if (callStack && callStack.length && stackFrame === callStack[0]) { + const topStackFrame = stackFrame.thread.getTopStackFrame(); + if (stackFrame.getId() === topStackFrame?.getId()) { result.push({ options: TOP_STACK_FRAME_MARGIN, range diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 5b95e4ce9c3..71cf0e20cc7 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; import { URI as uri } from 'vs/base/common/uri'; -import { first, distinct } from 'vs/base/common/arrays'; +import { distinct } from 'vs/base/common/arrays'; import * as errors from 'vs/base/common/errors'; import severity from 'vs/base/common/severity'; import * as aria from 'vs/base/browser/ui/aria/aria'; @@ -991,11 +991,8 @@ export function getStackFrameThreadAndSessionToFocus(model: IDebugModel, stackFr } } - if (!stackFrame) { - if (thread) { - const callStack = thread.getCallStack(); - stackFrame = first(callStack, sf => !!(sf && sf.source && sf.source.available && sf.source.presentationHint !== 'deemphasize'), undefined); - } + if (!stackFrame && thread) { + stackFrame = thread.getTopStackFrame(); } return { session, thread, stackFrame }; diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 163c77279e3..8353ae1ab17 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -288,6 +288,12 @@ export interface IThread extends ITreeElement { */ getCallStack(): ReadonlyArray; + + /** + * Gets the top stack frame that is not hidden if the callstack has already been received from the debug adapter + */ + getTopStackFrame(): IStackFrame | undefined; + /** * Invalidates the callstack cache */ diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index f45a693bbc7..0a5620c8d4b 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -10,7 +10,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { generateUuid } from 'vs/base/common/uuid'; import { RunOnceScheduler } from 'vs/base/common/async'; import { isString, isUndefinedOrNull } from 'vs/base/common/types'; -import { distinct, lastIndex } from 'vs/base/common/arrays'; +import { distinct, lastIndex, first } from 'vs/base/common/arrays'; import { Range, IRange } from 'vs/editor/common/core/range'; import { ITreeElement, IExpression, IExpressionContainer, IDebugSession, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IDebugModel, @@ -398,6 +398,10 @@ export class Thread implements IThread { return this.staleCallStack; } + getTopStackFrame(): IStackFrame | undefined { + return first(this.getCallStack(), sf => !!(sf && sf.source && sf.source.available && sf.source.presentationHint !== 'deemphasize'), undefined); + } + get stateLabel(): string { if (this.stoppedDetails) { return this.stoppedDetails.description || diff --git a/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts b/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts index 7dff5eea6e9..9449af5368e 100644 --- a/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts @@ -42,8 +42,8 @@ function createTwoStackFrames(session: DebugSession): { firstStackFrame: StackFr sourceReference: 11, }, 'aDebugSessionId'); - firstStackFrame = new StackFrame(thread, 1, firstSource, 'app.js', 'normal', { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 10 }, 1); - secondStackFrame = new StackFrame(thread, 1, secondSource, 'app.js', 'normal', { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 10 }, 1); + firstStackFrame = new StackFrame(thread, 0, firstSource, 'app.js', 'normal', { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 10 }, 0); + secondStackFrame = new StackFrame(thread, 1, secondSource, 'app2.js', 'normal', { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 10 }, 1); return { firstStackFrame, secondStackFrame }; } From 96f32b8d411d5d92660876868ca7233c59222d8d Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 12 Aug 2020 17:35:10 +0200 Subject: [PATCH 149/736] debug: move debugColors to separate file --- .../debug/browser/debug.contribution.ts | 188 +---------------- .../contrib/debug/browser/debugColors.ts | 190 ++++++++++++++++++ 2 files changed, 194 insertions(+), 184 deletions(-) create mode 100644 src/vs/workbench/contrib/debug/browser/debugColors.ts diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index abe68683a9c..ecf5818f86e 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -6,7 +6,6 @@ import 'vs/css!./media/debug.contribution'; import 'vs/css!./media/debugHover'; import * as nls from 'vs/nls'; -import { Color } from 'vs/base/common/color'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -44,8 +43,7 @@ import { VariablesView } from 'vs/workbench/contrib/debug/browser/variablesView' import { ClearReplAction, Repl } from 'vs/workbench/contrib/debug/browser/repl'; import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider'; import { WelcomeView } from 'vs/workbench/contrib/debug/browser/welcomeView'; -import { ThemeIcon, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { registerColor, foreground, badgeBackground, badgeForeground, listDeemphasizedForeground, contrastBorder, inputBorder, editorWarningForeground, errorForeground, editorInfoForeground } from 'vs/platform/theme/common/colorRegistry'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { DebugViewPaneContainer, OpenDebugConsoleAction } from 'vs/workbench/contrib/debug/browser/debugViewlet'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { CallStackEditorContribution } from 'vs/workbench/contrib/debug/browser/callStackEditorContribution'; @@ -57,6 +55,7 @@ import { StartDebugQuickAccessProvider } from 'vs/workbench/contrib/debug/browse import { DebugProgressContribution } from 'vs/workbench/contrib/debug/browser/debugProgress'; import { DebugTitleContribution } from 'vs/workbench/contrib/debug/browser/debugTitle'; import { Codicon } from 'vs/base/common/codicons'; +import { registerColors } from 'vs/workbench/contrib/debug/browser/debugColors'; class OpenDebugViewletAction extends ShowViewletAction { public static readonly ID = VIEWLET_ID; @@ -566,6 +565,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { order: 1 }); +registerColors(); + // Touch Bar if (isMacintosh) { @@ -592,184 +593,3 @@ if (isMacintosh) { registerTouchBarEntry(RESTART_SESSION_ID, RESTART_LABEL, 5, CONTEXT_IN_DEBUG_MODE, URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/restart-tb.png'))); registerTouchBarEntry(STOP_ID, STOP_LABEL, 6, CONTEXT_IN_DEBUG_MODE, URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/stop-tb.png'))); } - -// Color contributions - -const debugTokenExpressionName = registerColor('debugTokenExpression.name', { dark: '#c586c0', light: '#9b46b0', hc: foreground }, 'Foreground color for the token names shown in the debug views (ie. the Variables or Watch view).'); -const debugTokenExpressionValue = registerColor('debugTokenExpression.value', { dark: '#cccccc99', light: '#6c6c6ccc', hc: foreground }, 'Foreground color for the token values shown in the debug views (ie. the Variables or Watch view).'); -const debugTokenExpressionString = registerColor('debugTokenExpression.string', { dark: '#ce9178', light: '#a31515', hc: '#f48771' }, 'Foreground color for strings in the debug views (ie. the Variables or Watch view).'); -const debugTokenExpressionBoolean = registerColor('debugTokenExpression.boolean', { dark: '#4e94ce', light: '#0000ff', hc: '#75bdfe' }, 'Foreground color for booleans in the debug views (ie. the Variables or Watch view).'); -const debugTokenExpressionNumber = registerColor('debugTokenExpression.number', { dark: '#b5cea8', light: '#098658', hc: '#89d185' }, 'Foreground color for numbers in the debug views (ie. the Variables or Watch view).'); -const debugTokenExpressionError = registerColor('debugTokenExpression.error', { dark: '#f48771', light: '#e51400', hc: '#f48771' }, 'Foreground color for expression errors in the debug views (ie. the Variables or Watch view) and for error logs shown in the debug console.'); - -const debugViewExceptionLabelForeground = registerColor('debugView.exceptionLabelForeground', { dark: foreground, light: '#FFF', hc: foreground }, 'Foreground color for a label shown in the CALL STACK view when the debugger breaks on an exception.'); -const debugViewExceptionLabelBackground = registerColor('debugView.exceptionLabelBackground', { dark: '#6C2022', light: '#A31515', hc: '#6C2022' }, 'Background color for a label shown in the CALL STACK view when the debugger breaks on an exception.'); -const debugViewStateLabelForeground = registerColor('debugView.stateLabelForeground', { dark: foreground, light: foreground, hc: foreground }, 'Foreground color for a label in the CALL STACK view showing the current session\'s or thread\'s state.'); -const debugViewStateLabelBackground = registerColor('debugView.stateLabelBackground', { dark: '#88888844', light: '#88888844', hc: '#88888844' }, 'Background color for a label in the CALL STACK view showing the current session\'s or thread\'s state.'); -const debugViewValueChangedHighlight = registerColor('debugView.valueChangedHighlight', { dark: '#569CD6', light: '#569CD6', hc: '#569CD6' }, 'Color used to highlight value changes in the debug views (ie. in the Variables view).'); - -const debugConsoleInfoForeground = registerColor('debugConsole.infoForeground', { dark: editorInfoForeground, light: editorInfoForeground, hc: foreground }, 'Foreground color for info messages in debug REPL console.'); -const debugConsoleWarningForeground = registerColor('debugConsole.warningForeground', { dark: editorWarningForeground, light: editorWarningForeground, hc: '#008000' }, 'Foreground color for warning messages in debug REPL console.'); -const debugConsoleErrorForeground = registerColor('debugConsole.errorForeground', { dark: errorForeground, light: errorForeground, hc: errorForeground }, 'Foreground color for error messages in debug REPL console.'); -const debugConsoleSourceForeground = registerColor('debugConsole.sourceForeground', { dark: foreground, light: foreground, hc: foreground }, 'Foreground color for source filenames in debug REPL console.'); -const debugConsoleInputIconForeground = registerColor('debugConsoleInputIcon.foreground', { dark: foreground, light: foreground, hc: foreground }, 'Foreground color for debug console input marker icon.'); - -registerThemingParticipant((theme, collector) => { - // All these colours provide a default value so they will never be undefined, hence the `!` - const badgeBackgroundColor = theme.getColor(badgeBackground)!; - const badgeForegroundColor = theme.getColor(badgeForeground)!; - const listDeemphasizedForegroundColor = theme.getColor(listDeemphasizedForeground)!; - const debugViewExceptionLabelForegroundColor = theme.getColor(debugViewExceptionLabelForeground)!; - const debugViewExceptionLabelBackgroundColor = theme.getColor(debugViewExceptionLabelBackground)!; - const debugViewStateLabelForegroundColor = theme.getColor(debugViewStateLabelForeground)!; - const debugViewStateLabelBackgroundColor = theme.getColor(debugViewStateLabelBackground)!; - const debugViewValueChangedHighlightColor = theme.getColor(debugViewValueChangedHighlight)!; - - collector.addRule(` - /* Text colour of the call stack row's filename */ - .debug-pane .debug-call-stack .monaco-list-row:not(.selected) .stack-frame > .file .file-name { - color: ${listDeemphasizedForegroundColor} - } - - /* Line & column number "badge" for selected call stack row */ - .debug-pane .monaco-list-row.selected .line-number { - background-color: ${badgeBackgroundColor}; - color: ${badgeForegroundColor}; - } - - /* Line & column number "badge" for unselected call stack row (basically all other rows) */ - .debug-pane .line-number { - background-color: ${badgeBackgroundColor.transparent(0.6)}; - color: ${badgeForegroundColor.transparent(0.6)}; - } - - /* State "badge" displaying the active session's current state. - * Only visible when there are more active debug sessions/threads running. - */ - .debug-pane .debug-call-stack .thread > .state.label, - .debug-pane .debug-call-stack .session > .state.label, - .debug-pane .monaco-list-row.selected .thread > .state.label, - .debug-pane .monaco-list-row.selected .session > .state.label { - background-color: ${debugViewStateLabelBackgroundColor}; - color: ${debugViewStateLabelForegroundColor}; - } - - /* Info "badge" shown when the debugger pauses due to a thrown exception. */ - .debug-pane .debug-call-stack-title > .pause-message > .label.exception { - background-color: ${debugViewExceptionLabelBackgroundColor}; - color: ${debugViewExceptionLabelForegroundColor}; - } - - /* Animation of changed values in Debug viewlet */ - @keyframes debugViewletValueChanged { - 0% { background-color: ${debugViewValueChangedHighlightColor.transparent(0)} } - 5% { background-color: ${debugViewValueChangedHighlightColor.transparent(0.9)} } - 100% { background-color: ${debugViewValueChangedHighlightColor.transparent(0.3)} } - } - - .debug-pane .monaco-list-row .expression .value.changed { - background-color: ${debugViewValueChangedHighlightColor.transparent(0.3)}; - animation-name: debugViewletValueChanged; - animation-duration: 1s; - animation-fill-mode: forwards; - } - `); - - const contrastBorderColor = theme.getColor(contrastBorder); - - if (contrastBorderColor) { - collector.addRule(` - .debug-pane .line-number { - border: 1px solid ${contrastBorderColor}; - } - `); - } - - const tokenNameColor = theme.getColor(debugTokenExpressionName)!; - const tokenValueColor = theme.getColor(debugTokenExpressionValue)!; - const tokenStringColor = theme.getColor(debugTokenExpressionString)!; - const tokenBooleanColor = theme.getColor(debugTokenExpressionBoolean)!; - const tokenErrorColor = theme.getColor(debugTokenExpressionError)!; - const tokenNumberColor = theme.getColor(debugTokenExpressionNumber)!; - - collector.addRule(` - .monaco-workbench .monaco-list-row .expression .name { - color: ${tokenNameColor}; - } - - .monaco-workbench .monaco-list-row .expression .value, - .monaco-workbench .debug-hover-widget .value { - color: ${tokenValueColor}; - } - - .monaco-workbench .monaco-list-row .expression .value.string, - .monaco-workbench .debug-hover-widget .value.string { - color: ${tokenStringColor}; - } - - .monaco-workbench .monaco-list-row .expression .value.boolean, - .monaco-workbench .debug-hover-widget .value.boolean { - color: ${tokenBooleanColor}; - } - - .monaco-workbench .monaco-list-row .expression .error, - .monaco-workbench .debug-hover-widget .error, - .monaco-workbench .debug-pane .debug-variables .scope .error { - color: ${tokenErrorColor}; - } - - .monaco-workbench .monaco-list-row .expression .value.number, - .monaco-workbench .debug-hover-widget .value.number { - color: ${tokenNumberColor}; - } - `); - - const debugConsoleInputBorderColor = theme.getColor(inputBorder) || Color.fromHex('#80808060'); - const debugConsoleInfoForegroundColor = theme.getColor(debugConsoleInfoForeground)!; - const debugConsoleWarningForegroundColor = theme.getColor(debugConsoleWarningForeground)!; - const debugConsoleErrorForegroundColor = theme.getColor(debugConsoleErrorForeground)!; - const debugConsoleSourceForegroundColor = theme.getColor(debugConsoleSourceForeground)!; - const debugConsoleInputIconForegroundColor = theme.getColor(debugConsoleInputIconForeground)!; - - collector.addRule(` - .repl .repl-input-wrapper { - border-top: 1px solid ${debugConsoleInputBorderColor}; - } - - .monaco-workbench .repl .repl-tree .output .expression .value.info { - color: ${debugConsoleInfoForegroundColor}; - } - - .monaco-workbench .repl .repl-tree .output .expression .value.warn { - color: ${debugConsoleWarningForegroundColor}; - } - - .monaco-workbench .repl .repl-tree .output .expression .value.error { - color: ${debugConsoleErrorForegroundColor}; - } - - .monaco-workbench .repl .repl-tree .output .expression .source { - color: ${debugConsoleSourceForegroundColor}; - } - - .monaco-workbench .repl .repl-tree .monaco-tl-contents .arrow { - color: ${debugConsoleInputIconForegroundColor}; - } - `); - - if (!theme.defines(debugConsoleInputIconForeground)) { - collector.addRule(` - .monaco-workbench.vs .repl .repl-tree .monaco-tl-contents .arrow { - opacity: 0.25; - } - - .monaco-workbench.vs-dark .repl .repl-tree .monaco-tl-contents .arrow { - opacity: 0.4; - } - - .monaco-workbench.hc-black .repl .repl-tree .monaco-tl-contents .arrow { - opacity: 1; - } - `); - } -}); diff --git a/src/vs/workbench/contrib/debug/browser/debugColors.ts b/src/vs/workbench/contrib/debug/browser/debugColors.ts new file mode 100644 index 00000000000..df54038d02c --- /dev/null +++ b/src/vs/workbench/contrib/debug/browser/debugColors.ts @@ -0,0 +1,190 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerColor, foreground, editorInfoForeground, editorWarningForeground, errorForeground, badgeBackground, badgeForeground, listDeemphasizedForeground, contrastBorder, inputBorder } from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { Color } from 'vs/base/common/color'; + +export function registerColors() { + + const debugTokenExpressionName = registerColor('debugTokenExpression.name', { dark: '#c586c0', light: '#9b46b0', hc: foreground }, 'Foreground color for the token names shown in the debug views (ie. the Variables or Watch view).'); + const debugTokenExpressionValue = registerColor('debugTokenExpression.value', { dark: '#cccccc99', light: '#6c6c6ccc', hc: foreground }, 'Foreground color for the token values shown in the debug views (ie. the Variables or Watch view).'); + const debugTokenExpressionString = registerColor('debugTokenExpression.string', { dark: '#ce9178', light: '#a31515', hc: '#f48771' }, 'Foreground color for strings in the debug views (ie. the Variables or Watch view).'); + const debugTokenExpressionBoolean = registerColor('debugTokenExpression.boolean', { dark: '#4e94ce', light: '#0000ff', hc: '#75bdfe' }, 'Foreground color for booleans in the debug views (ie. the Variables or Watch view).'); + const debugTokenExpressionNumber = registerColor('debugTokenExpression.number', { dark: '#b5cea8', light: '#098658', hc: '#89d185' }, 'Foreground color for numbers in the debug views (ie. the Variables or Watch view).'); + const debugTokenExpressionError = registerColor('debugTokenExpression.error', { dark: '#f48771', light: '#e51400', hc: '#f48771' }, 'Foreground color for expression errors in the debug views (ie. the Variables or Watch view) and for error logs shown in the debug console.'); + + const debugViewExceptionLabelForeground = registerColor('debugView.exceptionLabelForeground', { dark: foreground, light: '#FFF', hc: foreground }, 'Foreground color for a label shown in the CALL STACK view when the debugger breaks on an exception.'); + const debugViewExceptionLabelBackground = registerColor('debugView.exceptionLabelBackground', { dark: '#6C2022', light: '#A31515', hc: '#6C2022' }, 'Background color for a label shown in the CALL STACK view when the debugger breaks on an exception.'); + const debugViewStateLabelForeground = registerColor('debugView.stateLabelForeground', { dark: foreground, light: foreground, hc: foreground }, 'Foreground color for a label in the CALL STACK view showing the current session\'s or thread\'s state.'); + const debugViewStateLabelBackground = registerColor('debugView.stateLabelBackground', { dark: '#88888844', light: '#88888844', hc: '#88888844' }, 'Background color for a label in the CALL STACK view showing the current session\'s or thread\'s state.'); + const debugViewValueChangedHighlight = registerColor('debugView.valueChangedHighlight', { dark: '#569CD6', light: '#569CD6', hc: '#569CD6' }, 'Color used to highlight value changes in the debug views (ie. in the Variables view).'); + + const debugConsoleInfoForeground = registerColor('debugConsole.infoForeground', { dark: editorInfoForeground, light: editorInfoForeground, hc: foreground }, 'Foreground color for info messages in debug REPL console.'); + const debugConsoleWarningForeground = registerColor('debugConsole.warningForeground', { dark: editorWarningForeground, light: editorWarningForeground, hc: '#008000' }, 'Foreground color for warning messages in debug REPL console.'); + const debugConsoleErrorForeground = registerColor('debugConsole.errorForeground', { dark: errorForeground, light: errorForeground, hc: errorForeground }, 'Foreground color for error messages in debug REPL console.'); + const debugConsoleSourceForeground = registerColor('debugConsole.sourceForeground', { dark: foreground, light: foreground, hc: foreground }, 'Foreground color for source filenames in debug REPL console.'); + const debugConsoleInputIconForeground = registerColor('debugConsoleInputIcon.foreground', { dark: foreground, light: foreground, hc: foreground }, 'Foreground color for debug console input marker icon.'); + + registerThemingParticipant((theme, collector) => { + // All these colours provide a default value so they will never be undefined, hence the `!` + const badgeBackgroundColor = theme.getColor(badgeBackground)!; + const badgeForegroundColor = theme.getColor(badgeForeground)!; + const listDeemphasizedForegroundColor = theme.getColor(listDeemphasizedForeground)!; + const debugViewExceptionLabelForegroundColor = theme.getColor(debugViewExceptionLabelForeground)!; + const debugViewExceptionLabelBackgroundColor = theme.getColor(debugViewExceptionLabelBackground)!; + const debugViewStateLabelForegroundColor = theme.getColor(debugViewStateLabelForeground)!; + const debugViewStateLabelBackgroundColor = theme.getColor(debugViewStateLabelBackground)!; + const debugViewValueChangedHighlightColor = theme.getColor(debugViewValueChangedHighlight)!; + + collector.addRule(` + /* Text colour of the call stack row's filename */ + .debug-pane .debug-call-stack .monaco-list-row:not(.selected) .stack-frame > .file .file-name { + color: ${listDeemphasizedForegroundColor} + } + + /* Line & column number "badge" for selected call stack row */ + .debug-pane .monaco-list-row.selected .line-number { + background-color: ${badgeBackgroundColor}; + color: ${badgeForegroundColor}; + } + + /* Line & column number "badge" for unselected call stack row (basically all other rows) */ + .debug-pane .line-number { + background-color: ${badgeBackgroundColor.transparent(0.6)}; + color: ${badgeForegroundColor.transparent(0.6)}; + } + + /* State "badge" displaying the active session's current state. + * Only visible when there are more active debug sessions/threads running. + */ + .debug-pane .debug-call-stack .thread > .state.label, + .debug-pane .debug-call-stack .session > .state.label, + .debug-pane .monaco-list-row.selected .thread > .state.label, + .debug-pane .monaco-list-row.selected .session > .state.label { + background-color: ${debugViewStateLabelBackgroundColor}; + color: ${debugViewStateLabelForegroundColor}; + } + + /* Info "badge" shown when the debugger pauses due to a thrown exception. */ + .debug-pane .debug-call-stack-title > .pause-message > .label.exception { + background-color: ${debugViewExceptionLabelBackgroundColor}; + color: ${debugViewExceptionLabelForegroundColor}; + } + + /* Animation of changed values in Debug viewlet */ + @keyframes debugViewletValueChanged { + 0% { background-color: ${debugViewValueChangedHighlightColor.transparent(0)} } + 5% { background-color: ${debugViewValueChangedHighlightColor.transparent(0.9)} } + 100% { background-color: ${debugViewValueChangedHighlightColor.transparent(0.3)} } + } + + .debug-pane .monaco-list-row .expression .value.changed { + background-color: ${debugViewValueChangedHighlightColor.transparent(0.3)}; + animation-name: debugViewletValueChanged; + animation-duration: 1s; + animation-fill-mode: forwards; + } + `); + + const contrastBorderColor = theme.getColor(contrastBorder); + + if (contrastBorderColor) { + collector.addRule(` + .debug-pane .line-number { + border: 1px solid ${contrastBorderColor}; + } + `); + } + + const tokenNameColor = theme.getColor(debugTokenExpressionName)!; + const tokenValueColor = theme.getColor(debugTokenExpressionValue)!; + const tokenStringColor = theme.getColor(debugTokenExpressionString)!; + const tokenBooleanColor = theme.getColor(debugTokenExpressionBoolean)!; + const tokenErrorColor = theme.getColor(debugTokenExpressionError)!; + const tokenNumberColor = theme.getColor(debugTokenExpressionNumber)!; + + collector.addRule(` + .monaco-workbench .monaco-list-row .expression .name { + color: ${tokenNameColor}; + } + + .monaco-workbench .monaco-list-row .expression .value, + .monaco-workbench .debug-hover-widget .value { + color: ${tokenValueColor}; + } + + .monaco-workbench .monaco-list-row .expression .value.string, + .monaco-workbench .debug-hover-widget .value.string { + color: ${tokenStringColor}; + } + + .monaco-workbench .monaco-list-row .expression .value.boolean, + .monaco-workbench .debug-hover-widget .value.boolean { + color: ${tokenBooleanColor}; + } + + .monaco-workbench .monaco-list-row .expression .error, + .monaco-workbench .debug-hover-widget .error, + .monaco-workbench .debug-pane .debug-variables .scope .error { + color: ${tokenErrorColor}; + } + + .monaco-workbench .monaco-list-row .expression .value.number, + .monaco-workbench .debug-hover-widget .value.number { + color: ${tokenNumberColor}; + } + `); + + const debugConsoleInputBorderColor = theme.getColor(inputBorder) || Color.fromHex('#80808060'); + const debugConsoleInfoForegroundColor = theme.getColor(debugConsoleInfoForeground)!; + const debugConsoleWarningForegroundColor = theme.getColor(debugConsoleWarningForeground)!; + const debugConsoleErrorForegroundColor = theme.getColor(debugConsoleErrorForeground)!; + const debugConsoleSourceForegroundColor = theme.getColor(debugConsoleSourceForeground)!; + const debugConsoleInputIconForegroundColor = theme.getColor(debugConsoleInputIconForeground)!; + + collector.addRule(` + .repl .repl-input-wrapper { + border-top: 1px solid ${debugConsoleInputBorderColor}; + } + + .monaco-workbench .repl .repl-tree .output .expression .value.info { + color: ${debugConsoleInfoForegroundColor}; + } + + .monaco-workbench .repl .repl-tree .output .expression .value.warn { + color: ${debugConsoleWarningForegroundColor}; + } + + .monaco-workbench .repl .repl-tree .output .expression .value.error { + color: ${debugConsoleErrorForegroundColor}; + } + + .monaco-workbench .repl .repl-tree .output .expression .source { + color: ${debugConsoleSourceForegroundColor}; + } + + .monaco-workbench .repl .repl-tree .monaco-tl-contents .arrow { + color: ${debugConsoleInputIconForegroundColor}; + } + `); + + if (!theme.defines(debugConsoleInputIconForeground)) { + collector.addRule(` + .monaco-workbench.vs .repl .repl-tree .monaco-tl-contents .arrow { + opacity: 0.25; + } + + .monaco-workbench.vs-dark .repl .repl-tree .monaco-tl-contents .arrow { + opacity: 0.4; + } + + .monaco-workbench.hc-black .repl .repl-tree .monaco-tl-contents .arrow { + opacity: 1; + } + `); + } + }); +} From 1b2939783c953a19186d3e63c8d5a83992267042 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 12 Aug 2020 17:43:45 +0200 Subject: [PATCH 150/736] debug registration: futher simplify debug.contribution.ts --- .../debug/browser/debug.contribution.ts | 38 ++----------------- .../contrib/debug/browser/debugViewlet.ts | 18 +++++++++ 2 files changed, 22 insertions(+), 34 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index ecf5818f86e..55513d3a568 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -10,10 +10,8 @@ import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionRegistryExtensions } from 'vs/workbench/common/actions'; -import { ShowViewletAction } from 'vs/workbench/browser/viewlet'; import { BreakpointsView } from 'vs/workbench/contrib/debug/browser/breakpointsView'; import { CallStackView } from 'vs/workbench/contrib/debug/browser/callStackView'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; @@ -21,11 +19,9 @@ import { IDebugService, VIEWLET_ID, DEBUG_PANEL_ID, CONTEXT_IN_DEBUG_MODE, INTERNAL_CONSOLE_OPTIONS_SCHEMA, CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_DEBUG_UX, BREAKPOINT_EDITOR_CONTRIBUTION_ID, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, } from 'vs/workbench/contrib/debug/common/debug'; -import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { StartAction, AddFunctionBreakpointAction, ConfigureAction, DisableAllBreakpointsAction, EnableAllBreakpointsAction, RemoveAllBreakpointsAction, RunAction, ReapplyBreakpointsAction, SelectAndStartAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { DebugToolBar } from 'vs/workbench/contrib/debug/browser/debugToolBar'; import * as service from 'vs/workbench/contrib/debug/browser/debugService'; -import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { registerCommands, ADD_CONFIGURATION_ID, TOGGLE_INLINE_BREAKPOINT_ID, COPY_STACK_TRACE_ID, REVERSE_CONTINUE_ID, STEP_BACK_ID, RESTART_SESSION_ID, TERMINATE_THREAD_ID, STEP_OVER_ID, STEP_INTO_ID, STEP_OUT_ID, PAUSE_ID, DISCONNECT_ID, STOP_ID, RESTART_FRAME_ID, CONTINUE_ID, FOCUS_REPL_ID, JUMP_TO_CURSOR_ID, RESTART_LABEL, STEP_INTO_LABEL, STEP_OVER_LABEL, STEP_OUT_LABEL, PAUSE_LABEL, DISCONNECT_LABEL, STOP_LABEL, CONTINUE_LABEL } from 'vs/workbench/contrib/debug/browser/debugCommands'; import { StatusBarColorProvider } from 'vs/workbench/contrib/debug/browser/statusbarColorProvider'; import { IViewsRegistry, Extensions as ViewExtensions, IViewContainersRegistry, ViewContainerLocation, ViewContainer } from 'vs/workbench/common/views'; @@ -35,7 +31,6 @@ import { URI } from 'vs/base/common/uri'; import { DebugStatusContribution } from 'vs/workbench/contrib/debug/browser/debugStatus'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration'; -import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { LoadedScriptsView } from 'vs/workbench/contrib/debug/browser/loadedScriptsView'; import { ADD_LOG_POINT_ID, TOGGLE_CONDITIONAL_BREAKPOINT_ID, TOGGLE_BREAKPOINT_ID, RunToCursorAction } from 'vs/workbench/contrib/debug/browser/debugEditorActions'; import { WatchExpressionsView } from 'vs/workbench/contrib/debug/browser/watchExpressionsView'; @@ -44,7 +39,7 @@ import { ClearReplAction, Repl } from 'vs/workbench/contrib/debug/browser/repl'; import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider'; import { WelcomeView } from 'vs/workbench/contrib/debug/browser/welcomeView'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { DebugViewPaneContainer, OpenDebugConsoleAction } from 'vs/workbench/contrib/debug/browser/debugViewlet'; +import { DebugViewPaneContainer, OpenDebugConsoleAction, OpenDebugViewletAction } from 'vs/workbench/contrib/debug/browser/debugViewlet'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { CallStackEditorContribution } from 'vs/workbench/contrib/debug/browser/callStackEditorContribution'; import { BreakpointEditorContribution } from 'vs/workbench/contrib/debug/browser/breakpointEditorContribution'; @@ -57,21 +52,6 @@ import { DebugTitleContribution } from 'vs/workbench/contrib/debug/browser/debug import { Codicon } from 'vs/base/common/codicons'; import { registerColors } from 'vs/workbench/contrib/debug/browser/debugColors'; -class OpenDebugViewletAction extends ShowViewletAction { - public static readonly ID = VIEWLET_ID; - public static readonly LABEL = nls.localize('toggleDebugViewlet', "Show Run and Debug"); - - constructor( - id: string, - label: string, - @IViewletService viewletService: IViewletService, - @IEditorGroupsService editorGroupService: IEditorGroupsService, - @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService - ) { - super(id, label, VIEWLET_ID, viewletService, editorGroupService, layoutService); - } -} - const viewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ id: VIEWLET_ID, name: nls.localize('run', "Run"), @@ -81,13 +61,6 @@ const viewContainer = Registry.as(ViewExtensions.ViewCo order: 2 }, ViewContainerLocation.Sidebar); -const openViewletKb: IKeybindings = { - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_D -}; -const openPanelKb: IKeybindings = { - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Y -}; - // register repl panel const VIEW_CONTAINER: ViewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ @@ -96,10 +69,7 @@ const VIEW_CONTAINER: ViewContainer = Registry.as(ViewE icon: Codicon.debugConsole.classNames, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [DEBUG_PANEL_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), storageId: DEBUG_PANEL_ID, - focusCommand: { - id: OpenDebugConsoleAction.ID, - keybindings: openPanelKb - }, + focusCommand: { id: OpenDebugConsoleAction.ID }, order: 2, hideIfEmpty: true }, ViewContainerLocation.Panel); @@ -126,8 +96,8 @@ registerCommands(); // register action to open viewlet const registry = Registry.as(WorkbenchActionRegistryExtensions.WorkbenchActions); -registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenDebugConsoleAction, openPanelKb), 'View: Debug Console', nls.localize('view', "View")); -registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenDebugViewletAction, openViewletKb), 'View: Show Run and Debug', nls.localize('view', "View")); +registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenDebugConsoleAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Y }), 'View: Debug Console', nls.localize('view', "View")); +registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenDebugViewletAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_D }), 'View: Show Run and Debug', nls.localize('view', "View")); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugToolBar, LifecyclePhase.Restored); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugContentProvider, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts index bbe7ac03099..d32774028e5 100644 --- a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts @@ -31,6 +31,9 @@ import { IViewDescriptorService, IViewsService } from 'vs/workbench/common/views import { WelcomeView } from 'vs/workbench/contrib/debug/browser/welcomeView'; import { ToggleViewAction } from 'vs/workbench/browser/actions/layoutActions'; import { RunOnceScheduler } from 'vs/base/common/async'; +import { ShowViewletAction } from 'vs/workbench/browser/viewlet'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; export class DebugViewPaneContainer extends ViewPaneContainer { @@ -248,3 +251,18 @@ export class OpenDebugConsoleAction extends ToggleViewAction { super(id, label, REPL_VIEW_ID, viewsService, viewDescriptorService, contextKeyService, layoutService, 'codicon-debug-console'); } } + +export class OpenDebugViewletAction extends ShowViewletAction { + public static readonly ID = VIEWLET_ID; + public static readonly LABEL = nls.localize('toggleDebugViewlet', "Show Run and Debug"); + + constructor( + id: string, + label: string, + @IViewletService viewletService: IViewletService, + @IEditorGroupsService editorGroupService: IEditorGroupsService, + @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService + ) { + super(id, label, VIEWLET_ID, viewletService, editorGroupService, layoutService); + } +} From 7b1bfa133532dd6e5243d1ee148c00127a5837c2 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Wed, 12 Aug 2020 09:42:55 -0700 Subject: [PATCH 151/736] Preserve custom webview file icon in HC (fixes #104434) --- src/vs/workbench/contrib/webview/browser/webviewIconManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/webview/browser/webviewIconManager.ts b/src/vs/workbench/contrib/webview/browser/webviewIconManager.ts index 6091fed2b34..e57c8c16cb0 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewIconManager.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewIconManager.ts @@ -54,7 +54,7 @@ export class WebviewIconManager { try { cssRules.push( `.monaco-workbench.vs ${webviewSelector} { content: ""; background-image: ${dom.asCSSUrl(value.light)}; }`, - `.monaco-workbench.vs-dark ${webviewSelector} { content: ""; background-image: ${dom.asCSSUrl(value.dark)}; }` + `.monaco-workbench.vs-dark ${webviewSelector}, .monaco-workbench.hc-black ${webviewSelector} { content: ""; background-image: ${dom.asCSSUrl(value.dark)}; }` ); } catch { // noop From 8943ec4bf38f3e350500c9195cfbd8608a7fe794 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 12 Aug 2020 18:07:43 +0200 Subject: [PATCH 152/736] restructure debug.contrubtion.ts into logical registration parts --- .../debug/browser/debug.contribution.ts | 1013 +++++++++-------- .../debug/browser/debugEditorActions.ts | 22 +- 2 files changed, 526 insertions(+), 509 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 55513d3a568..6200b45045a 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -32,7 +32,7 @@ import { DebugStatusContribution } from 'vs/workbench/contrib/debug/browser/debu import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration'; import { LoadedScriptsView } from 'vs/workbench/contrib/debug/browser/loadedScriptsView'; -import { ADD_LOG_POINT_ID, TOGGLE_CONDITIONAL_BREAKPOINT_ID, TOGGLE_BREAKPOINT_ID, RunToCursorAction } from 'vs/workbench/contrib/debug/browser/debugEditorActions'; +import { ADD_LOG_POINT_ID, TOGGLE_CONDITIONAL_BREAKPOINT_ID, TOGGLE_BREAKPOINT_ID, RunToCursorAction, registerEditorActions } from 'vs/workbench/contrib/debug/browser/debugEditorActions'; import { WatchExpressionsView } from 'vs/workbench/contrib/debug/browser/watchExpressionsView'; import { VariablesView } from 'vs/workbench/contrib/debug/browser/variablesView'; import { ClearReplAction, Repl } from 'vs/workbench/contrib/debug/browser/repl'; @@ -52,514 +52,529 @@ import { DebugTitleContribution } from 'vs/workbench/contrib/debug/browser/debug import { Codicon } from 'vs/base/common/codicons'; import { registerColors } from 'vs/workbench/contrib/debug/browser/debugColors'; -const viewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ - id: VIEWLET_ID, - name: nls.localize('run', "Run"), - ctorDescriptor: new SyncDescriptor(DebugViewPaneContainer), - icon: Codicon.debugAlt.classNames, - alwaysUseContainerInfo: true, - order: 2 -}, ViewContainerLocation.Sidebar); - -// register repl panel - -const VIEW_CONTAINER: ViewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ - id: DEBUG_PANEL_ID, - name: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, 'Debug Console'), - icon: Codicon.debugConsole.classNames, - ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [DEBUG_PANEL_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), - storageId: DEBUG_PANEL_ID, - focusCommand: { id: OpenDebugConsoleAction.ID }, - order: 2, - hideIfEmpty: true -}, ViewContainerLocation.Panel); - -Registry.as(ViewExtensions.ViewsRegistry).registerViews([{ - id: REPL_VIEW_ID, - name: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, 'Debug Console'), - containerIcon: Codicon.debugConsole.classNames, - canToggleVisibility: false, - canMoveView: true, - ctorDescriptor: new SyncDescriptor(Repl), -}], VIEW_CONTAINER); - -// Register default debug views -const viewsRegistry = Registry.as(ViewExtensions.ViewsRegistry); -viewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(VariablesView), order: 10, weight: 40, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusVariablesView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); -viewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(WatchExpressionsView), order: 20, weight: 10, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusWatchView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); -viewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(CallStackView), order: 30, weight: 30, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusCallStackView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); -viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(BreakpointsView), order: 40, weight: 20, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' }, when: ContextKeyExpr.or(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer); -viewsRegistry.registerViews([{ id: WelcomeView.ID, name: WelcomeView.LABEL, containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(WelcomeView), order: 1, weight: 40, canToggleVisibility: true, when: CONTEXT_DEBUG_UX.isEqualTo('simple') }], viewContainer); -viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(LoadedScriptsView), order: 35, weight: 5, canToggleVisibility: true, canMoveView: true, collapsed: true, when: ContextKeyExpr.and(CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer); - -registerCommands(); - -// register action to open viewlet const registry = Registry.as(WorkbenchActionRegistryExtensions.WorkbenchActions); -registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenDebugConsoleAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Y }), 'View: Debug Console', nls.localize('view', "View")); -registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenDebugViewletAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_D }), 'View: Show Run and Debug', nls.localize('view', "View")); - -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugToolBar, LifecyclePhase.Restored); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugContentProvider, LifecyclePhase.Eventually); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(StatusBarColorProvider, LifecyclePhase.Eventually); - -const debugCategory = nls.localize('debugCategory', "Debug"); -const runCategroy = nls.localize('runCategory', "Run"); - -registry.registerWorkbenchAction(SyncActionDescriptor.from(StartAction, { primary: KeyCode.F5 }, CONTEXT_IN_DEBUG_MODE.toNegated()), 'Debug: Start Debugging', debugCategory); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ConfigureAction), 'Debug: Open launch.json', debugCategory); -registry.registerWorkbenchAction(SyncActionDescriptor.from(AddFunctionBreakpointAction), 'Debug: Add Function Breakpoint', debugCategory); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ReapplyBreakpointsAction), 'Debug: Reapply All Breakpoints', debugCategory); -registry.registerWorkbenchAction(SyncActionDescriptor.from(RunAction, { primary: KeyMod.CtrlCmd | KeyCode.F5, mac: { primary: KeyMod.WinCtrl | KeyCode.F5 } }), 'Run: Start Without Debugging', runCategroy); -registry.registerWorkbenchAction(SyncActionDescriptor.from(RemoveAllBreakpointsAction), 'Debug: Remove All Breakpoints', debugCategory); -registry.registerWorkbenchAction(SyncActionDescriptor.from(EnableAllBreakpointsAction), 'Debug: Enable All Breakpoints', debugCategory); -registry.registerWorkbenchAction(SyncActionDescriptor.from(DisableAllBreakpointsAction), 'Debug: Disable All Breakpoints', debugCategory); -registry.registerWorkbenchAction(SyncActionDescriptor.from(SelectAndStartAction), 'Debug: Select and Start Debugging', debugCategory); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ClearReplAction), 'Debug: Clear Console', debugCategory); - -const registerDebugCommandPaletteItem = (id: string, title: string, when?: ContextKeyExpression, precondition?: ContextKeyExpression) => { - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - when, - command: { - id, - title: `Debug: ${title}`, - precondition - } - }); -}; - -registerDebugCommandPaletteItem(RESTART_SESSION_ID, RESTART_LABEL); -registerDebugCommandPaletteItem(TERMINATE_THREAD_ID, nls.localize('terminateThread', "Terminate Thread"), CONTEXT_IN_DEBUG_MODE); -registerDebugCommandPaletteItem(STEP_OVER_ID, STEP_OVER_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); -registerDebugCommandPaletteItem(STEP_INTO_ID, STEP_INTO_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); -registerDebugCommandPaletteItem(STEP_OUT_ID, STEP_OUT_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); -registerDebugCommandPaletteItem(PAUSE_ID, PAUSE_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('running')); -registerDebugCommandPaletteItem(DISCONNECT_ID, DISCONNECT_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_FOCUSED_SESSION_IS_ATTACH); -registerDebugCommandPaletteItem(STOP_ID, STOP_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_FOCUSED_SESSION_IS_ATTACH.toNegated()); -registerDebugCommandPaletteItem(CONTINUE_ID, CONTINUE_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); -registerDebugCommandPaletteItem(FOCUS_REPL_ID, nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugFocusConsole' }, 'Focus on Debug Console View')); -registerDebugCommandPaletteItem(JUMP_TO_CURSOR_ID, nls.localize('jumpToCursor', "Jump to Cursor"), CONTEXT_JUMP_TO_CURSOR_SUPPORTED); -registerDebugCommandPaletteItem(JUMP_TO_CURSOR_ID, nls.localize('SetNextStatement', "Set Next Statement"), CONTEXT_JUMP_TO_CURSOR_SUPPORTED); -registerDebugCommandPaletteItem(RunToCursorAction.ID, RunToCursorAction.LABEL, ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped'))); -registerDebugCommandPaletteItem(TOGGLE_INLINE_BREAKPOINT_ID, nls.localize('inlineBreakpoint', "Inline Breakpoint")); - - -// Register Quick Access -Registry.as(QuickAccessExtensions.Quickaccess).registerQuickAccessProvider({ - ctor: StartDebugQuickAccessProvider, - prefix: StartDebugQuickAccessProvider.PREFIX, - contextKey: 'inLaunchConfigurationsPicker', - placeholder: nls.localize('startDebugPlaceholder', "Type the name of a launch configuration to run."), - helpEntries: [{ description: nls.localize('startDebuggingHelp', "Start Debugging"), needsEditor: false }] -}); - // register service registerSingleton(IDebugService, service.DebugService, true); - -// Register configuration -const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); -configurationRegistry.registerConfiguration({ - id: 'debug', - order: 20, - title: nls.localize('debugConfigurationTitle', "Debug"), - type: 'object', - properties: { - 'debug.allowBreakpointsEverywhere': { - type: 'boolean', - description: nls.localize({ comment: ['This is the description for a setting'], key: 'allowBreakpointsEverywhere' }, "Allow setting breakpoints in any file."), - default: false - }, - 'debug.openExplorerOnEnd': { - type: 'boolean', - description: nls.localize({ comment: ['This is the description for a setting'], key: 'openExplorerOnEnd' }, "Automatically open the explorer view at the end of a debug session."), - default: false - }, - 'debug.inlineValues': { - type: 'boolean', - description: nls.localize({ comment: ['This is the description for a setting'], key: 'inlineValues' }, "Show variable values inline in editor while debugging."), - default: false - }, - 'debug.toolBarLocation': { - enum: ['floating', 'docked', 'hidden'], - markdownDescription: nls.localize({ comment: ['This is the description for a setting'], key: 'toolBarLocation' }, "Controls the location of the debug toolbar. Either `floating` in all views, `docked` in the debug view, or `hidden`."), - default: 'floating' - }, - 'debug.showInStatusBar': { - enum: ['never', 'always', 'onFirstSessionStart'], - enumDescriptions: [nls.localize('never', "Never show debug in status bar"), nls.localize('always', "Always show debug in status bar"), nls.localize('onFirstSessionStart', "Show debug in status bar only after debug was started for the first time")], - description: nls.localize({ comment: ['This is the description for a setting'], key: 'showInStatusBar' }, "Controls when the debug status bar should be visible."), - default: 'onFirstSessionStart' - }, - 'debug.internalConsoleOptions': INTERNAL_CONSOLE_OPTIONS_SCHEMA, - 'debug.console.closeOnEnd': { - type: 'boolean', - description: nls.localize('debug.console.closeOnEnd', "Controls if the debug console should be automatically closed when the debug session ends."), - default: false - }, - 'debug.openDebug': { - enum: ['neverOpen', 'openOnSessionStart', 'openOnFirstSessionStart', 'openOnDebugBreak'], - default: 'openOnFirstSessionStart', - description: nls.localize('openDebug', "Controls when the debug view should open.") - }, - 'debug.showSubSessionsInToolBar': { - type: 'boolean', - description: nls.localize({ comment: ['This is the description for a setting'], key: 'showSubSessionsInToolBar' }, "Controls whether the debug sub-sessions are shown in the debug tool bar. When this setting is false the stop command on a sub-session will also stop the parent session."), - default: false - }, - 'debug.console.fontSize': { - type: 'number', - description: nls.localize('debug.console.fontSize', "Controls the font size in pixels in the debug console."), - default: isMacintosh ? 12 : 14, - }, - 'debug.console.fontFamily': { - type: 'string', - description: nls.localize('debug.console.fontFamily', "Controls the font family in the debug console."), - default: 'default' - }, - 'debug.console.lineHeight': { - type: 'number', - description: nls.localize('debug.console.lineHeight', "Controls the line height in pixels in the debug console. Use 0 to compute the line height from the font size."), - default: 0 - }, - 'debug.console.wordWrap': { - type: 'boolean', - description: nls.localize('debug.console.wordWrap', "Controls if the lines should wrap in the debug console."), - default: true - }, - 'debug.console.historySuggestions': { - type: 'boolean', - description: nls.localize('debug.console.historySuggestions', "Controls if the debug console should suggest previously typed input."), - default: true - }, - 'launch': { - type: 'object', - description: nls.localize({ comment: ['This is the description for a setting'], key: 'launch' }, "Global debug launch configuration. Should be used as an alternative to 'launch.json' that is shared across workspaces."), - default: { configurations: [], compounds: [] }, - $ref: launchSchemaId - }, - 'debug.focusWindowOnBreak': { - type: 'boolean', - description: nls.localize('debug.focusWindowOnBreak', "Controls whether the workbench window should be focused when the debugger breaks."), - default: true - }, - 'debug.onTaskErrors': { - enum: ['debugAnyway', 'showErrors', 'prompt', 'abort'], - enumDescriptions: [nls.localize('debugAnyway', "Ignore task errors and start debugging."), nls.localize('showErrors', "Show the Problems view and do not start debugging."), nls.localize('prompt', "Prompt user."), nls.localize('cancel', "Cancel debugging.")], - description: nls.localize('debug.onTaskErrors', "Controls what to do when errors are encountered after running a preLaunchTask."), - default: 'prompt' - }, - 'debug.showBreakpointsInOverviewRuler': { - type: 'boolean', - description: nls.localize({ comment: ['This is the description for a setting'], key: 'showBreakpointsInOverviewRuler' }, "Controls whether breakpoints should be shown in the overview ruler."), - default: false - }, - 'debug.showInlineBreakpointCandidates': { - type: 'boolean', - description: nls.localize({ comment: ['This is the description for a setting'], key: 'showInlineBreakpointCandidates' }, "Controls whether inline breakpoints candidate decorations should be shown in the editor while debugging."), - default: true - } - } -}); - -// Register Debug Workbench Contributions -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugStatusContribution, LifecyclePhase.Eventually); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugProgressContribution, LifecyclePhase.Eventually); -if (isWeb) { - Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugTitleContribution, LifecyclePhase.Eventually); -} - -// Debug toolbar - -const registerDebugToolBarItem = (id: string, title: string, order: number, icon: { light?: URI, dark?: URI } | ThemeIcon, when?: ContextKeyExpression, precondition?: ContextKeyExpression) => { - MenuRegistry.appendMenuItem(MenuId.DebugToolBar, { - group: 'navigation', - when, - order, - command: { - id, - title, - icon, - precondition - } - }); -}; - -registerDebugToolBarItem(CONTINUE_ID, CONTINUE_LABEL, 10, { id: 'codicon/debug-continue' }, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); -registerDebugToolBarItem(PAUSE_ID, PAUSE_LABEL, 10, { id: 'codicon/debug-pause' }, CONTEXT_DEBUG_STATE.notEqualsTo('stopped')); -registerDebugToolBarItem(STOP_ID, STOP_LABEL, 70, { id: 'codicon/debug-stop' }, CONTEXT_FOCUSED_SESSION_IS_ATTACH.toNegated()); -registerDebugToolBarItem(DISCONNECT_ID, DISCONNECT_LABEL, 70, { id: 'codicon/debug-disconnect' }, CONTEXT_FOCUSED_SESSION_IS_ATTACH); -registerDebugToolBarItem(STEP_OVER_ID, STEP_OVER_LABEL, 20, { id: 'codicon/debug-step-over' }, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); -registerDebugToolBarItem(STEP_INTO_ID, STEP_INTO_LABEL, 30, { id: 'codicon/debug-step-into' }, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); -registerDebugToolBarItem(STEP_OUT_ID, STEP_OUT_LABEL, 40, { id: 'codicon/debug-step-out' }, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); -registerDebugToolBarItem(RESTART_SESSION_ID, RESTART_LABEL, 60, { id: 'codicon/debug-restart' }); -registerDebugToolBarItem(STEP_BACK_ID, nls.localize('stepBackDebug', "Step Back"), 50, { id: 'codicon/debug-step-back' }, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); -registerDebugToolBarItem(REVERSE_CONTINUE_ID, nls.localize('reverseContinue', "Reverse"), 60, { id: 'codicon/debug-reverse-continue' }, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); - -// Debug callstack context menu -const registerDebugCallstackItem = (id: string, title: string, order: number, when?: ContextKeyExpression, precondition?: ContextKeyExpression, group = 'navigation') => { - MenuRegistry.appendMenuItem(MenuId.DebugCallStackContext, { - group, - when, - order, - command: { - id, - title, - precondition - } - }); -}; -registerDebugCallstackItem(RESTART_SESSION_ID, RESTART_LABEL, 10, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('session')); -registerDebugCallstackItem(STOP_ID, STOP_LABEL, 20, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('session')); -registerDebugCallstackItem(PAUSE_ID, PAUSE_LABEL, 10, ContextKeyExpr.and(CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), CONTEXT_DEBUG_STATE.isEqualTo('running'))); -registerDebugCallstackItem(CONTINUE_ID, CONTINUE_LABEL, 10, ContextKeyExpr.and(CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), CONTEXT_DEBUG_STATE.isEqualTo('stopped'))); -registerDebugCallstackItem(STEP_OVER_ID, STEP_OVER_LABEL, 20, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), CONTEXT_DEBUG_STATE.isEqualTo('stopped')); -registerDebugCallstackItem(STEP_INTO_ID, STEP_INTO_LABEL, 30, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), CONTEXT_DEBUG_STATE.isEqualTo('stopped')); -registerDebugCallstackItem(STEP_OUT_ID, STEP_OUT_LABEL, 40, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), CONTEXT_DEBUG_STATE.isEqualTo('stopped')); -registerDebugCallstackItem(TERMINATE_THREAD_ID, nls.localize('terminateThread', "Terminate Thread"), 10, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), undefined, 'termination'); -registerDebugCallstackItem(RESTART_FRAME_ID, nls.localize('restartFrame', "Restart Frame"), 10, ContextKeyExpr.and(CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('stackFrame'), CONTEXT_RESTART_FRAME_SUPPORTED)); -registerDebugCallstackItem(COPY_STACK_TRACE_ID, nls.localize('copyStackTrace', "Copy Call Stack"), 20, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('stackFrame')); - -// Editor contributions - -registerEditorContribution('editor.contrib.callStack', CallStackEditorContribution); -registerEditorContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID, BreakpointEditorContribution); - -// View menu - -MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '3_views', - command: { - id: VIEWLET_ID, - title: nls.localize({ key: 'miViewRun', comment: ['&& denotes a mnemonic'] }, "&&Run") - }, - order: 4 -}); - -MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '4_panels', - command: { - id: OpenDebugConsoleAction.ID, - title: nls.localize({ key: 'miToggleDebugConsole', comment: ['&& denotes a mnemonic'] }, "De&&bug Console") - }, - order: 2 -}); - -// Debug menu - -MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '1_debug', - command: { - id: StartAction.ID, - title: nls.localize({ key: 'miStartDebugging', comment: ['&& denotes a mnemonic'] }, "&&Start Debugging") - }, - order: 1 -}); - -MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '1_debug', - command: { - id: RunAction.ID, - title: nls.localize({ key: 'miRun', comment: ['&& denotes a mnemonic'] }, "Run &&Without Debugging") - }, - order: 2 -}); - -MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '1_debug', - command: { - id: STOP_ID, - title: nls.localize({ key: 'miStopDebugging', comment: ['&& denotes a mnemonic'] }, "&&Stop Debugging"), - precondition: CONTEXT_IN_DEBUG_MODE - }, - order: 3 -}); - -MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '1_debug', - command: { - id: RESTART_SESSION_ID, - title: nls.localize({ key: 'miRestart Debugging', comment: ['&& denotes a mnemonic'] }, "&&Restart Debugging"), - precondition: CONTEXT_IN_DEBUG_MODE - }, - order: 4 -}); - -// Configuration -MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '2_configuration', - command: { - id: ConfigureAction.ID, - title: nls.localize({ key: 'miOpenConfigurations', comment: ['&& denotes a mnemonic'] }, "Open &&Configurations") - }, - order: 1 -}); - -MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '2_configuration', - command: { - id: ADD_CONFIGURATION_ID, - title: nls.localize({ key: 'miAddConfiguration', comment: ['&& denotes a mnemonic'] }, "A&&dd Configuration...") - }, - order: 2 -}); - -// Step Commands -MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '3_step', - command: { - id: STEP_OVER_ID, - title: nls.localize({ key: 'miStepOver', comment: ['&& denotes a mnemonic'] }, "Step &&Over"), - precondition: CONTEXT_DEBUG_STATE.isEqualTo('stopped') - }, - order: 1 -}); - -MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '3_step', - command: { - id: STEP_INTO_ID, - title: nls.localize({ key: 'miStepInto', comment: ['&& denotes a mnemonic'] }, "Step &&Into"), - precondition: CONTEXT_DEBUG_STATE.isEqualTo('stopped') - }, - order: 2 -}); - -MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '3_step', - command: { - id: STEP_OUT_ID, - title: nls.localize({ key: 'miStepOut', comment: ['&& denotes a mnemonic'] }, "Step O&&ut"), - precondition: CONTEXT_DEBUG_STATE.isEqualTo('stopped') - }, - order: 3 -}); - -MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '3_step', - command: { - id: CONTINUE_ID, - title: nls.localize({ key: 'miContinue', comment: ['&& denotes a mnemonic'] }, "&&Continue"), - precondition: CONTEXT_DEBUG_STATE.isEqualTo('stopped') - }, - order: 4 -}); - -// New Breakpoints -MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '4_new_breakpoint', - command: { - id: TOGGLE_BREAKPOINT_ID, - title: nls.localize({ key: 'miToggleBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Breakpoint") - }, - order: 1 -}); - -MenuRegistry.appendMenuItem(MenuId.MenubarNewBreakpointMenu, { - group: '1_breakpoints', - command: { - id: TOGGLE_CONDITIONAL_BREAKPOINT_ID, - title: nls.localize({ key: 'miConditionalBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Conditional Breakpoint...") - }, - order: 1 -}); - -MenuRegistry.appendMenuItem(MenuId.MenubarNewBreakpointMenu, { - group: '1_breakpoints', - command: { - id: TOGGLE_INLINE_BREAKPOINT_ID, - title: nls.localize({ key: 'miInlineBreakpoint', comment: ['&& denotes a mnemonic'] }, "Inline Breakp&&oint") - }, - order: 2 -}); - -MenuRegistry.appendMenuItem(MenuId.MenubarNewBreakpointMenu, { - group: '1_breakpoints', - command: { - id: AddFunctionBreakpointAction.ID, - title: nls.localize({ key: 'miFunctionBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Function Breakpoint...") - }, - order: 3 -}); - -MenuRegistry.appendMenuItem(MenuId.MenubarNewBreakpointMenu, { - group: '1_breakpoints', - command: { - id: ADD_LOG_POINT_ID, - title: nls.localize({ key: 'miLogPoint', comment: ['&& denotes a mnemonic'] }, "&&Logpoint...") - }, - order: 4 -}); - -MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '4_new_breakpoint', - title: nls.localize({ key: 'miNewBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&New Breakpoint"), - submenu: MenuId.MenubarNewBreakpointMenu, - order: 2 -}); - -// Modify Breakpoints -MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '5_breakpoints', - command: { - id: EnableAllBreakpointsAction.ID, - title: nls.localize({ key: 'miEnableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "&&Enable All Breakpoints") - }, - order: 1 -}); - -MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '5_breakpoints', - command: { - id: DisableAllBreakpointsAction.ID, - title: nls.localize({ key: 'miDisableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Disable A&&ll Breakpoints") - }, - order: 2 -}); - -MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '5_breakpoints', - command: { - id: RemoveAllBreakpointsAction.ID, - title: nls.localize({ key: 'miRemoveAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Remove &&All Breakpoints") - }, - order: 3 -}); - -// Install Debuggers -MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: 'z_install', - command: { - id: 'debug.installAdditionalDebuggers', - title: nls.localize({ key: 'miInstallAdditionalDebuggers', comment: ['&& denotes a mnemonic'] }, "&&Install Additional Debuggers...") - }, - order: 1 -}); +registerDebugView(); registerColors(); +registerCommands(); +registerCommandsAndActions(); +registerContributions(); +registerDebugMenu(); +registerDebugPanel(); +registerConfiguration(); +registerEditorActions(); -// Touch Bar -if (isMacintosh) { +function registerContributions(): void { + // Register Debug Workbench Contributions + Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugStatusContribution, LifecyclePhase.Eventually); + Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugProgressContribution, LifecyclePhase.Eventually); + if (isWeb) { + Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugTitleContribution, LifecyclePhase.Eventually); + } + Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugToolBar, LifecyclePhase.Restored); + Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugContentProvider, LifecyclePhase.Eventually); + Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(StatusBarColorProvider, LifecyclePhase.Eventually); - const registerTouchBarEntry = (id: string, title: string, order: number, when: ContextKeyExpression | undefined, iconUri: URI) => { - MenuRegistry.appendMenuItem(MenuId.TouchBarContext, { + // Editor contributions + registerEditorContribution('editor.contrib.callStack', CallStackEditorContribution); + registerEditorContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID, BreakpointEditorContribution); + + // Register Quick Access + Registry.as(QuickAccessExtensions.Quickaccess).registerQuickAccessProvider({ + ctor: StartDebugQuickAccessProvider, + prefix: StartDebugQuickAccessProvider.PREFIX, + contextKey: 'inLaunchConfigurationsPicker', + placeholder: nls.localize('startDebugPlaceholder', "Type the name of a launch configuration to run."), + helpEntries: [{ description: nls.localize('startDebuggingHelp', "Start Debugging"), needsEditor: false }] + }); +} + +function registerCommandsAndActions(): void { + + const debugCategory = nls.localize('debugCategory', "Debug"); + const runCategroy = nls.localize('runCategory', "Run"); + + registry.registerWorkbenchAction(SyncActionDescriptor.from(StartAction, { primary: KeyCode.F5 }, CONTEXT_IN_DEBUG_MODE.toNegated()), 'Debug: Start Debugging', debugCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.from(ConfigureAction), 'Debug: Open launch.json', debugCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.from(AddFunctionBreakpointAction), 'Debug: Add Function Breakpoint', debugCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.from(ReapplyBreakpointsAction), 'Debug: Reapply All Breakpoints', debugCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.from(RunAction, { primary: KeyMod.CtrlCmd | KeyCode.F5, mac: { primary: KeyMod.WinCtrl | KeyCode.F5 } }), 'Run: Start Without Debugging', runCategroy); + registry.registerWorkbenchAction(SyncActionDescriptor.from(RemoveAllBreakpointsAction), 'Debug: Remove All Breakpoints', debugCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.from(EnableAllBreakpointsAction), 'Debug: Enable All Breakpoints', debugCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.from(DisableAllBreakpointsAction), 'Debug: Disable All Breakpoints', debugCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.from(SelectAndStartAction), 'Debug: Select and Start Debugging', debugCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.from(ClearReplAction), 'Debug: Clear Console', debugCategory); + + const registerDebugCommandPaletteItem = (id: string, title: string, when?: ContextKeyExpression, precondition?: ContextKeyExpression) => { + MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + when, command: { id, - title, - icon: { dark: iconUri } - }, - when, - group: '9_debug', - order + title: `Debug: ${title}`, + precondition + } }); }; - registerTouchBarEntry(StartAction.ID, StartAction.LABEL, 0, CONTEXT_IN_DEBUG_MODE.toNegated(), URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/continue-tb.png'))); - registerTouchBarEntry(RunAction.ID, RunAction.LABEL, 1, CONTEXT_IN_DEBUG_MODE.toNegated(), URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/continue-without-debugging-tb.png'))); - registerTouchBarEntry(CONTINUE_ID, CONTINUE_LABEL, 0, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/continue-tb.png'))); - registerTouchBarEntry(PAUSE_ID, PAUSE_LABEL, 1, ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.notEquals('debugState', 'stopped')), URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/pause-tb.png'))); - registerTouchBarEntry(STEP_OVER_ID, STEP_OVER_LABEL, 2, CONTEXT_IN_DEBUG_MODE, URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/stepover-tb.png'))); - registerTouchBarEntry(STEP_INTO_ID, STEP_INTO_LABEL, 3, CONTEXT_IN_DEBUG_MODE, URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/stepinto-tb.png'))); - registerTouchBarEntry(STEP_OUT_ID, STEP_OUT_LABEL, 4, CONTEXT_IN_DEBUG_MODE, URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/stepout-tb.png'))); - registerTouchBarEntry(RESTART_SESSION_ID, RESTART_LABEL, 5, CONTEXT_IN_DEBUG_MODE, URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/restart-tb.png'))); - registerTouchBarEntry(STOP_ID, STOP_LABEL, 6, CONTEXT_IN_DEBUG_MODE, URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/stop-tb.png'))); + registerDebugCommandPaletteItem(RESTART_SESSION_ID, RESTART_LABEL); + registerDebugCommandPaletteItem(TERMINATE_THREAD_ID, nls.localize('terminateThread', "Terminate Thread"), CONTEXT_IN_DEBUG_MODE); + registerDebugCommandPaletteItem(STEP_OVER_ID, STEP_OVER_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugCommandPaletteItem(STEP_INTO_ID, STEP_INTO_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugCommandPaletteItem(STEP_OUT_ID, STEP_OUT_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugCommandPaletteItem(PAUSE_ID, PAUSE_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('running')); + registerDebugCommandPaletteItem(DISCONNECT_ID, DISCONNECT_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_FOCUSED_SESSION_IS_ATTACH); + registerDebugCommandPaletteItem(STOP_ID, STOP_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_FOCUSED_SESSION_IS_ATTACH.toNegated()); + registerDebugCommandPaletteItem(CONTINUE_ID, CONTINUE_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugCommandPaletteItem(FOCUS_REPL_ID, nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugFocusConsole' }, 'Focus on Debug Console View')); + registerDebugCommandPaletteItem(JUMP_TO_CURSOR_ID, nls.localize('jumpToCursor', "Jump to Cursor"), CONTEXT_JUMP_TO_CURSOR_SUPPORTED); + registerDebugCommandPaletteItem(JUMP_TO_CURSOR_ID, nls.localize('SetNextStatement', "Set Next Statement"), CONTEXT_JUMP_TO_CURSOR_SUPPORTED); + registerDebugCommandPaletteItem(RunToCursorAction.ID, RunToCursorAction.LABEL, ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped'))); + registerDebugCommandPaletteItem(TOGGLE_INLINE_BREAKPOINT_ID, nls.localize('inlineBreakpoint', "Inline Breakpoint")); + + // Debug toolbar + + const registerDebugToolBarItem = (id: string, title: string, order: number, icon: { light?: URI, dark?: URI } | ThemeIcon, when?: ContextKeyExpression, precondition?: ContextKeyExpression) => { + MenuRegistry.appendMenuItem(MenuId.DebugToolBar, { + group: 'navigation', + when, + order, + command: { + id, + title, + icon, + precondition + } + }); + }; + + registerDebugToolBarItem(CONTINUE_ID, CONTINUE_LABEL, 10, { id: 'codicon/debug-continue' }, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugToolBarItem(PAUSE_ID, PAUSE_LABEL, 10, { id: 'codicon/debug-pause' }, CONTEXT_DEBUG_STATE.notEqualsTo('stopped')); + registerDebugToolBarItem(STOP_ID, STOP_LABEL, 70, { id: 'codicon/debug-stop' }, CONTEXT_FOCUSED_SESSION_IS_ATTACH.toNegated()); + registerDebugToolBarItem(DISCONNECT_ID, DISCONNECT_LABEL, 70, { id: 'codicon/debug-disconnect' }, CONTEXT_FOCUSED_SESSION_IS_ATTACH); + registerDebugToolBarItem(STEP_OVER_ID, STEP_OVER_LABEL, 20, { id: 'codicon/debug-step-over' }, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugToolBarItem(STEP_INTO_ID, STEP_INTO_LABEL, 30, { id: 'codicon/debug-step-into' }, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugToolBarItem(STEP_OUT_ID, STEP_OUT_LABEL, 40, { id: 'codicon/debug-step-out' }, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugToolBarItem(RESTART_SESSION_ID, RESTART_LABEL, 60, { id: 'codicon/debug-restart' }); + registerDebugToolBarItem(STEP_BACK_ID, nls.localize('stepBackDebug', "Step Back"), 50, { id: 'codicon/debug-step-back' }, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugToolBarItem(REVERSE_CONTINUE_ID, nls.localize('reverseContinue', "Reverse"), 60, { id: 'codicon/debug-reverse-continue' }, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + + // Debug callstack context menu + const registerDebugCallstackItem = (id: string, title: string, order: number, when?: ContextKeyExpression, precondition?: ContextKeyExpression, group = 'navigation') => { + MenuRegistry.appendMenuItem(MenuId.DebugCallStackContext, { + group, + when, + order, + command: { + id, + title, + precondition + } + }); + }; + registerDebugCallstackItem(RESTART_SESSION_ID, RESTART_LABEL, 10, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('session')); + registerDebugCallstackItem(STOP_ID, STOP_LABEL, 20, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('session')); + registerDebugCallstackItem(PAUSE_ID, PAUSE_LABEL, 10, ContextKeyExpr.and(CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), CONTEXT_DEBUG_STATE.isEqualTo('running'))); + registerDebugCallstackItem(CONTINUE_ID, CONTINUE_LABEL, 10, ContextKeyExpr.and(CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), CONTEXT_DEBUG_STATE.isEqualTo('stopped'))); + registerDebugCallstackItem(STEP_OVER_ID, STEP_OVER_LABEL, 20, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugCallstackItem(STEP_INTO_ID, STEP_INTO_LABEL, 30, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugCallstackItem(STEP_OUT_ID, STEP_OUT_LABEL, 40, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugCallstackItem(TERMINATE_THREAD_ID, nls.localize('terminateThread', "Terminate Thread"), 10, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), undefined, 'termination'); + registerDebugCallstackItem(RESTART_FRAME_ID, nls.localize('restartFrame', "Restart Frame"), 10, ContextKeyExpr.and(CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('stackFrame'), CONTEXT_RESTART_FRAME_SUPPORTED)); + registerDebugCallstackItem(COPY_STACK_TRACE_ID, nls.localize('copyStackTrace', "Copy Call Stack"), 20, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('stackFrame')); + + // Touch Bar + if (isMacintosh) { + + const registerTouchBarEntry = (id: string, title: string, order: number, when: ContextKeyExpression | undefined, iconUri: URI) => { + MenuRegistry.appendMenuItem(MenuId.TouchBarContext, { + command: { + id, + title, + icon: { dark: iconUri } + }, + when, + group: '9_debug', + order + }); + }; + + registerTouchBarEntry(StartAction.ID, StartAction.LABEL, 0, CONTEXT_IN_DEBUG_MODE.toNegated(), URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/continue-tb.png'))); + registerTouchBarEntry(RunAction.ID, RunAction.LABEL, 1, CONTEXT_IN_DEBUG_MODE.toNegated(), URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/continue-without-debugging-tb.png'))); + registerTouchBarEntry(CONTINUE_ID, CONTINUE_LABEL, 0, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/continue-tb.png'))); + registerTouchBarEntry(PAUSE_ID, PAUSE_LABEL, 1, ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.notEquals('debugState', 'stopped')), URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/pause-tb.png'))); + registerTouchBarEntry(STEP_OVER_ID, STEP_OVER_LABEL, 2, CONTEXT_IN_DEBUG_MODE, URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/stepover-tb.png'))); + registerTouchBarEntry(STEP_INTO_ID, STEP_INTO_LABEL, 3, CONTEXT_IN_DEBUG_MODE, URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/stepinto-tb.png'))); + registerTouchBarEntry(STEP_OUT_ID, STEP_OUT_LABEL, 4, CONTEXT_IN_DEBUG_MODE, URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/stepout-tb.png'))); + registerTouchBarEntry(RESTART_SESSION_ID, RESTART_LABEL, 5, CONTEXT_IN_DEBUG_MODE, URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/restart-tb.png'))); + registerTouchBarEntry(STOP_ID, STOP_LABEL, 6, CONTEXT_IN_DEBUG_MODE, URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/stop-tb.png'))); + } +} + +function registerDebugMenu(): void { + // View menu + + MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '3_views', + command: { + id: VIEWLET_ID, + title: nls.localize({ key: 'miViewRun', comment: ['&& denotes a mnemonic'] }, "&&Run") + }, + order: 4 + }); + + MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '4_panels', + command: { + id: OpenDebugConsoleAction.ID, + title: nls.localize({ key: 'miToggleDebugConsole', comment: ['&& denotes a mnemonic'] }, "De&&bug Console") + }, + order: 2 + }); + + // Debug menu + + MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '1_debug', + command: { + id: StartAction.ID, + title: nls.localize({ key: 'miStartDebugging', comment: ['&& denotes a mnemonic'] }, "&&Start Debugging") + }, + order: 1 + }); + + MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '1_debug', + command: { + id: RunAction.ID, + title: nls.localize({ key: 'miRun', comment: ['&& denotes a mnemonic'] }, "Run &&Without Debugging") + }, + order: 2 + }); + + MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '1_debug', + command: { + id: STOP_ID, + title: nls.localize({ key: 'miStopDebugging', comment: ['&& denotes a mnemonic'] }, "&&Stop Debugging"), + precondition: CONTEXT_IN_DEBUG_MODE + }, + order: 3 + }); + + MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '1_debug', + command: { + id: RESTART_SESSION_ID, + title: nls.localize({ key: 'miRestart Debugging', comment: ['&& denotes a mnemonic'] }, "&&Restart Debugging"), + precondition: CONTEXT_IN_DEBUG_MODE + }, + order: 4 + }); + + // Configuration + MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '2_configuration', + command: { + id: ConfigureAction.ID, + title: nls.localize({ key: 'miOpenConfigurations', comment: ['&& denotes a mnemonic'] }, "Open &&Configurations") + }, + order: 1 + }); + + MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '2_configuration', + command: { + id: ADD_CONFIGURATION_ID, + title: nls.localize({ key: 'miAddConfiguration', comment: ['&& denotes a mnemonic'] }, "A&&dd Configuration...") + }, + order: 2 + }); + + // Step Commands + MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '3_step', + command: { + id: STEP_OVER_ID, + title: nls.localize({ key: 'miStepOver', comment: ['&& denotes a mnemonic'] }, "Step &&Over"), + precondition: CONTEXT_DEBUG_STATE.isEqualTo('stopped') + }, + order: 1 + }); + + MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '3_step', + command: { + id: STEP_INTO_ID, + title: nls.localize({ key: 'miStepInto', comment: ['&& denotes a mnemonic'] }, "Step &&Into"), + precondition: CONTEXT_DEBUG_STATE.isEqualTo('stopped') + }, + order: 2 + }); + + MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '3_step', + command: { + id: STEP_OUT_ID, + title: nls.localize({ key: 'miStepOut', comment: ['&& denotes a mnemonic'] }, "Step O&&ut"), + precondition: CONTEXT_DEBUG_STATE.isEqualTo('stopped') + }, + order: 3 + }); + + MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '3_step', + command: { + id: CONTINUE_ID, + title: nls.localize({ key: 'miContinue', comment: ['&& denotes a mnemonic'] }, "&&Continue"), + precondition: CONTEXT_DEBUG_STATE.isEqualTo('stopped') + }, + order: 4 + }); + + // New Breakpoints + MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '4_new_breakpoint', + command: { + id: TOGGLE_BREAKPOINT_ID, + title: nls.localize({ key: 'miToggleBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Breakpoint") + }, + order: 1 + }); + + MenuRegistry.appendMenuItem(MenuId.MenubarNewBreakpointMenu, { + group: '1_breakpoints', + command: { + id: TOGGLE_CONDITIONAL_BREAKPOINT_ID, + title: nls.localize({ key: 'miConditionalBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Conditional Breakpoint...") + }, + order: 1 + }); + + MenuRegistry.appendMenuItem(MenuId.MenubarNewBreakpointMenu, { + group: '1_breakpoints', + command: { + id: TOGGLE_INLINE_BREAKPOINT_ID, + title: nls.localize({ key: 'miInlineBreakpoint', comment: ['&& denotes a mnemonic'] }, "Inline Breakp&&oint") + }, + order: 2 + }); + + MenuRegistry.appendMenuItem(MenuId.MenubarNewBreakpointMenu, { + group: '1_breakpoints', + command: { + id: AddFunctionBreakpointAction.ID, + title: nls.localize({ key: 'miFunctionBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Function Breakpoint...") + }, + order: 3 + }); + + MenuRegistry.appendMenuItem(MenuId.MenubarNewBreakpointMenu, { + group: '1_breakpoints', + command: { + id: ADD_LOG_POINT_ID, + title: nls.localize({ key: 'miLogPoint', comment: ['&& denotes a mnemonic'] }, "&&Logpoint...") + }, + order: 4 + }); + + MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '4_new_breakpoint', + title: nls.localize({ key: 'miNewBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&New Breakpoint"), + submenu: MenuId.MenubarNewBreakpointMenu, + order: 2 + }); + + // Modify Breakpoints + MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '5_breakpoints', + command: { + id: EnableAllBreakpointsAction.ID, + title: nls.localize({ key: 'miEnableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "&&Enable All Breakpoints") + }, + order: 1 + }); + + MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '5_breakpoints', + command: { + id: DisableAllBreakpointsAction.ID, + title: nls.localize({ key: 'miDisableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Disable A&&ll Breakpoints") + }, + order: 2 + }); + + MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '5_breakpoints', + command: { + id: RemoveAllBreakpointsAction.ID, + title: nls.localize({ key: 'miRemoveAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Remove &&All Breakpoints") + }, + order: 3 + }); + + // Install Debuggers + MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: 'z_install', + command: { + id: 'debug.installAdditionalDebuggers', + title: nls.localize({ key: 'miInstallAdditionalDebuggers', comment: ['&& denotes a mnemonic'] }, "&&Install Additional Debuggers...") + }, + order: 1 + }); +} + +function registerDebugPanel(): void { + // register repl panel + + const VIEW_CONTAINER: ViewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ + id: DEBUG_PANEL_ID, + name: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, 'Debug Console'), + icon: Codicon.debugConsole.classNames, + ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [DEBUG_PANEL_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), + storageId: DEBUG_PANEL_ID, + focusCommand: { id: OpenDebugConsoleAction.ID }, + order: 2, + hideIfEmpty: true + }, ViewContainerLocation.Panel); + + Registry.as(ViewExtensions.ViewsRegistry).registerViews([{ + id: REPL_VIEW_ID, + name: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, 'Debug Console'), + containerIcon: Codicon.debugConsole.classNames, + canToggleVisibility: false, + canMoveView: true, + ctorDescriptor: new SyncDescriptor(Repl), + }], VIEW_CONTAINER); + + registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenDebugConsoleAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Y }), 'View: Debug Console', nls.localize('view', "View")); +} + +function registerDebugView(): void { + const viewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ + id: VIEWLET_ID, + name: nls.localize('run', "Run"), + ctorDescriptor: new SyncDescriptor(DebugViewPaneContainer), + icon: Codicon.debugAlt.classNames, + alwaysUseContainerInfo: true, + order: 2 + }, ViewContainerLocation.Sidebar); + registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenDebugViewletAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_D }), 'View: Show Run and Debug', nls.localize('view', "View")); + + // Register default debug views + const viewsRegistry = Registry.as(ViewExtensions.ViewsRegistry); + viewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(VariablesView), order: 10, weight: 40, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusVariablesView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); + viewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(WatchExpressionsView), order: 20, weight: 10, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusWatchView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); + viewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(CallStackView), order: 30, weight: 30, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusCallStackView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); + viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(BreakpointsView), order: 40, weight: 20, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' }, when: ContextKeyExpr.or(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer); + viewsRegistry.registerViews([{ id: WelcomeView.ID, name: WelcomeView.LABEL, containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(WelcomeView), order: 1, weight: 40, canToggleVisibility: true, when: CONTEXT_DEBUG_UX.isEqualTo('simple') }], viewContainer); + viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(LoadedScriptsView), order: 35, weight: 5, canToggleVisibility: true, canMoveView: true, collapsed: true, when: ContextKeyExpr.and(CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer); +} + +function registerConfiguration(): void { + // Register configuration + const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + configurationRegistry.registerConfiguration({ + id: 'debug', + order: 20, + title: nls.localize('debugConfigurationTitle', "Debug"), + type: 'object', + properties: { + 'debug.allowBreakpointsEverywhere': { + type: 'boolean', + description: nls.localize({ comment: ['This is the description for a setting'], key: 'allowBreakpointsEverywhere' }, "Allow setting breakpoints in any file."), + default: false + }, + 'debug.openExplorerOnEnd': { + type: 'boolean', + description: nls.localize({ comment: ['This is the description for a setting'], key: 'openExplorerOnEnd' }, "Automatically open the explorer view at the end of a debug session."), + default: false + }, + 'debug.inlineValues': { + type: 'boolean', + description: nls.localize({ comment: ['This is the description for a setting'], key: 'inlineValues' }, "Show variable values inline in editor while debugging."), + default: false + }, + 'debug.toolBarLocation': { + enum: ['floating', 'docked', 'hidden'], + markdownDescription: nls.localize({ comment: ['This is the description for a setting'], key: 'toolBarLocation' }, "Controls the location of the debug toolbar. Either `floating` in all views, `docked` in the debug view, or `hidden`."), + default: 'floating' + }, + 'debug.showInStatusBar': { + enum: ['never', 'always', 'onFirstSessionStart'], + enumDescriptions: [nls.localize('never', "Never show debug in status bar"), nls.localize('always', "Always show debug in status bar"), nls.localize('onFirstSessionStart', "Show debug in status bar only after debug was started for the first time")], + description: nls.localize({ comment: ['This is the description for a setting'], key: 'showInStatusBar' }, "Controls when the debug status bar should be visible."), + default: 'onFirstSessionStart' + }, + 'debug.internalConsoleOptions': INTERNAL_CONSOLE_OPTIONS_SCHEMA, + 'debug.console.closeOnEnd': { + type: 'boolean', + description: nls.localize('debug.console.closeOnEnd', "Controls if the debug console should be automatically closed when the debug session ends."), + default: false + }, + 'debug.openDebug': { + enum: ['neverOpen', 'openOnSessionStart', 'openOnFirstSessionStart', 'openOnDebugBreak'], + default: 'openOnFirstSessionStart', + description: nls.localize('openDebug', "Controls when the debug view should open.") + }, + 'debug.showSubSessionsInToolBar': { + type: 'boolean', + description: nls.localize({ comment: ['This is the description for a setting'], key: 'showSubSessionsInToolBar' }, "Controls whether the debug sub-sessions are shown in the debug tool bar. When this setting is false the stop command on a sub-session will also stop the parent session."), + default: false + }, + 'debug.console.fontSize': { + type: 'number', + description: nls.localize('debug.console.fontSize', "Controls the font size in pixels in the debug console."), + default: isMacintosh ? 12 : 14, + }, + 'debug.console.fontFamily': { + type: 'string', + description: nls.localize('debug.console.fontFamily', "Controls the font family in the debug console."), + default: 'default' + }, + 'debug.console.lineHeight': { + type: 'number', + description: nls.localize('debug.console.lineHeight', "Controls the line height in pixels in the debug console. Use 0 to compute the line height from the font size."), + default: 0 + }, + 'debug.console.wordWrap': { + type: 'boolean', + description: nls.localize('debug.console.wordWrap', "Controls if the lines should wrap in the debug console."), + default: true + }, + 'debug.console.historySuggestions': { + type: 'boolean', + description: nls.localize('debug.console.historySuggestions', "Controls if the debug console should suggest previously typed input."), + default: true + }, + 'launch': { + type: 'object', + description: nls.localize({ comment: ['This is the description for a setting'], key: 'launch' }, "Global debug launch configuration. Should be used as an alternative to 'launch.json' that is shared across workspaces."), + default: { configurations: [], compounds: [] }, + $ref: launchSchemaId + }, + 'debug.focusWindowOnBreak': { + type: 'boolean', + description: nls.localize('debug.focusWindowOnBreak', "Controls whether the workbench window should be focused when the debugger breaks."), + default: true + }, + 'debug.onTaskErrors': { + enum: ['debugAnyway', 'showErrors', 'prompt', 'abort'], + enumDescriptions: [nls.localize('debugAnyway', "Ignore task errors and start debugging."), nls.localize('showErrors', "Show the Problems view and do not start debugging."), nls.localize('prompt', "Prompt user."), nls.localize('cancel', "Cancel debugging.")], + description: nls.localize('debug.onTaskErrors', "Controls what to do when errors are encountered after running a preLaunchTask."), + default: 'prompt' + }, + 'debug.showBreakpointsInOverviewRuler': { + type: 'boolean', + description: nls.localize({ comment: ['This is the description for a setting'], key: 'showBreakpointsInOverviewRuler' }, "Controls whether breakpoints should be shown in the overview ruler."), + default: false + }, + 'debug.showInlineBreakpointCandidates': { + type: 'boolean', + description: nls.localize({ comment: ['This is the description for a setting'], key: 'showInlineBreakpointCandidates' }, "Controls whether inline breakpoints candidate decorations should be shown in the editor while debugging."), + default: true + } + } + }); } diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index 2954fda0f33..184cf2c5912 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -356,13 +356,15 @@ class GoToPreviousBreakpointAction extends GoToBreakpointAction { } } -registerEditorAction(ToggleBreakpointAction); -registerEditorAction(ConditionalBreakpointAction); -registerEditorAction(LogPointAction); -registerEditorAction(RunToCursorAction); -registerEditorAction(StepIntoTargetsAction); -registerEditorAction(SelectionToReplAction); -registerEditorAction(SelectionToWatchExpressionsAction); -registerEditorAction(ShowDebugHoverAction); -registerEditorAction(GoToNextBreakpointAction); -registerEditorAction(GoToPreviousBreakpointAction); +export function registerEditorActions(): void { + registerEditorAction(ToggleBreakpointAction); + registerEditorAction(ConditionalBreakpointAction); + registerEditorAction(LogPointAction); + registerEditorAction(RunToCursorAction); + registerEditorAction(StepIntoTargetsAction); + registerEditorAction(SelectionToReplAction); + registerEditorAction(SelectionToWatchExpressionsAction); + registerEditorAction(ShowDebugHoverAction); + registerEditorAction(GoToNextBreakpointAction); + registerEditorAction(GoToPreviousBreakpointAction); +} From f04ef75438abb0bb2add345388e7567fa493b4a4 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 12 Aug 2020 18:47:28 +0200 Subject: [PATCH 153/736] debug: register contributions only once debug adapter extension got registered --- .../debug/browser/debug.contribution.ts | 40 +++++++++++-------- .../browser/debugConfigurationManager.ts | 7 ++++ .../debug/browser/debugEditorContribution.ts | 7 +--- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 6200b45045a..4db8bcb67a3 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -17,11 +17,11 @@ import { CallStackView } from 'vs/workbench/contrib/debug/browser/callStackView' import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { IDebugService, VIEWLET_ID, DEBUG_PANEL_ID, CONTEXT_IN_DEBUG_MODE, INTERNAL_CONSOLE_OPTIONS_SCHEMA, - CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_DEBUG_UX, BREAKPOINT_EDITOR_CONTRIBUTION_ID, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, + CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_DEBUG_UX, BREAKPOINT_EDITOR_CONTRIBUTION_ID, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, EDITOR_CONTRIBUTION_ID, } from 'vs/workbench/contrib/debug/common/debug'; import { StartAction, AddFunctionBreakpointAction, ConfigureAction, DisableAllBreakpointsAction, EnableAllBreakpointsAction, RemoveAllBreakpointsAction, RunAction, ReapplyBreakpointsAction, SelectAndStartAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { DebugToolBar } from 'vs/workbench/contrib/debug/browser/debugToolBar'; -import * as service from 'vs/workbench/contrib/debug/browser/debugService'; +import { DebugService } from 'vs/workbench/contrib/debug/browser/debugService'; import { registerCommands, ADD_CONFIGURATION_ID, TOGGLE_INLINE_BREAKPOINT_ID, COPY_STACK_TRACE_ID, REVERSE_CONTINUE_ID, STEP_BACK_ID, RESTART_SESSION_ID, TERMINATE_THREAD_ID, STEP_OVER_ID, STEP_INTO_ID, STEP_OUT_ID, PAUSE_ID, DISCONNECT_ID, STOP_ID, RESTART_FRAME_ID, CONTINUE_ID, FOCUS_REPL_ID, JUMP_TO_CURSOR_ID, RESTART_LABEL, STEP_INTO_LABEL, STEP_OVER_LABEL, STEP_OUT_LABEL, PAUSE_LABEL, DISCONNECT_LABEL, STOP_LABEL, CONTINUE_LABEL } from 'vs/workbench/contrib/debug/browser/debugCommands'; import { StatusBarColorProvider } from 'vs/workbench/contrib/debug/browser/statusbarColorProvider'; import { IViewsRegistry, Extensions as ViewExtensions, IViewContainersRegistry, ViewContainerLocation, ViewContainer } from 'vs/workbench/common/views'; @@ -51,22 +51,27 @@ import { DebugProgressContribution } from 'vs/workbench/contrib/debug/browser/de import { DebugTitleContribution } from 'vs/workbench/contrib/debug/browser/debugTitle'; import { Codicon } from 'vs/base/common/codicons'; import { registerColors } from 'vs/workbench/contrib/debug/browser/debugColors'; +import { debugAdapterRegisteredEmitter } from 'vs/workbench/contrib/debug/browser/debugConfigurationManager'; +import { DebugEditorContribution } from 'vs/workbench/contrib/debug/browser/debugEditorContribution'; const registry = Registry.as(WorkbenchActionRegistryExtensions.WorkbenchActions); // register service -registerSingleton(IDebugService, service.DebugService, true); +debugAdapterRegisteredEmitter.event(() => { + // Register these contributions lazily only once a debug adapter extension has been registered + registerWorkbenchContributions(); + registerColors(); + registerCommands(); + registerCommandsAndActions(); + registerDebugMenu(); + registerDebugPanel(); + registerEditorActions(); +}); +registerSingleton(IDebugService, DebugService, true); registerDebugView(); - -registerColors(); -registerCommands(); -registerCommandsAndActions(); -registerContributions(); -registerDebugMenu(); -registerDebugPanel(); registerConfiguration(); -registerEditorActions(); +regsiterEditorContributions(); -function registerContributions(): void { +function registerWorkbenchContributions(): void { // Register Debug Workbench Contributions Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugStatusContribution, LifecyclePhase.Eventually); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugProgressContribution, LifecyclePhase.Eventually); @@ -77,10 +82,6 @@ function registerContributions(): void { Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugContentProvider, LifecyclePhase.Eventually); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(StatusBarColorProvider, LifecyclePhase.Eventually); - // Editor contributions - registerEditorContribution('editor.contrib.callStack', CallStackEditorContribution); - registerEditorContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID, BreakpointEditorContribution); - // Register Quick Access Registry.as(QuickAccessExtensions.Quickaccess).registerQuickAccessProvider({ ctor: StartDebugQuickAccessProvider, @@ -89,6 +90,13 @@ function registerContributions(): void { placeholder: nls.localize('startDebugPlaceholder', "Type the name of a launch configuration to run."), helpEntries: [{ description: nls.localize('startDebuggingHelp', "Start Debugging"), needsEditor: false }] }); + +} + +function regsiterEditorContributions(): void { + registerEditorContribution('editor.contrib.callStack', CallStackEditorContribution); + registerEditorContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID, BreakpointEditorContribution); + registerEditorContribution(EDITOR_CONTRIBUTION_ID, DebugEditorContribution); } function registerCommandsAndActions(): void { diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index e0ab9d8b528..987674fa14c 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -49,6 +49,8 @@ const DEBUG_SELECTED_ROOT = 'debug.selectedroot'; interface IDynamicPickItem { label: string, launch: ILaunch, config: IConfig } +export const debugAdapterRegisteredEmitter = new Emitter(); + export class ConfigurationManager implements IConfigurationManager { private debuggers: Debugger[]; private breakpointModeIdsSet = new Set(); @@ -95,7 +97,12 @@ export class ConfigurationManager implements IConfigurationManager { // debuggers registerDebugAdapterFactory(debugTypes: string[], debugAdapterLauncher: IDebugAdapterFactory): IDisposable { + const firstTimeRegistration = debugTypes.length && this.debugAdapterFactories.size === 0; debugTypes.forEach(debugType => this.debugAdapterFactories.set(debugType, debugAdapterLauncher)); + if (firstTimeRegistration) { + debugAdapterRegisteredEmitter.fire(); + } + return { dispose: () => { debugTypes.forEach(debugType => this.debugAdapterFactories.delete(debugType)); diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index 711e00f179d..b8d3efa299b 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -14,7 +14,6 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { StandardTokenType } from 'vs/editor/common/modes'; import { DEFAULT_WORD_REGEXP } from 'vs/editor/common/model/wordHelper'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType, IPartialEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; -import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { IDecorationOptions } from 'vs/editor/common/editorCommon'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { Range } from 'vs/editor/common/core/range'; @@ -22,7 +21,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IDebugEditorContribution, IDebugService, State, EDITOR_CONTRIBUTION_ID, IStackFrame, IDebugConfiguration, IExpression, IExceptionInfo, IDebugSession } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugEditorContribution, IDebugService, State, IStackFrame, IDebugConfiguration, IExpression, IExceptionInfo, IDebugSession } from 'vs/workbench/contrib/debug/common/debug'; import { ExceptionWidget } from 'vs/workbench/contrib/debug/browser/exceptionWidget'; import { FloatingClickWidget } from 'vs/workbench/browser/parts/editor/editorWidgets'; import { Position } from 'vs/editor/common/core/position'; @@ -164,7 +163,7 @@ function getWordToLineNumbersMap(model: ITextModel | null): Map Date: Wed, 12 Aug 2020 09:47:45 -0700 Subject: [PATCH 154/736] Add token to needs more info closer --- .github/workflows/needs-more-info-closer.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/needs-more-info-closer.yml b/.github/workflows/needs-more-info-closer.yml index d143dec9536..95543a868ad 100644 --- a/.github/workflows/needs-more-info-closer.yml +++ b/.github/workflows/needs-more-info-closer.yml @@ -21,6 +21,7 @@ jobs: uses: ./actions/needs-more-info-closer with: appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} label: needs more info closeDays: 7 additionalTeam: "cleidigh|usernamehw|gjsjohnmurray|IllusionMH" From eeae1ae6c46f5ea73ae95752f83b4aa722b6606a Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Wed, 12 Aug 2020 10:00:35 -0700 Subject: [PATCH 155/736] Bring ping days down to 80. Getting close to final taarget! (60) --- .github/workflows/needs-more-info-closer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/needs-more-info-closer.yml b/.github/workflows/needs-more-info-closer.yml index 95543a868ad..6cd2c6db7f7 100644 --- a/.github/workflows/needs-more-info-closer.yml +++ b/.github/workflows/needs-more-info-closer.yml @@ -26,5 +26,5 @@ jobs: closeDays: 7 additionalTeam: "cleidigh|usernamehw|gjsjohnmurray|IllusionMH" closeComment: "This issue has been closed automatically because it needs more information and has not had recent activity. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" - pingDays: 100 + pingDays: 60 pingComment: "Hey @${assignee}, this issue might need further attention.\n\n@${author}, you can help us out by closing this issue if the problem no longer exists, or adding more information." From 0b03a79adfde0a7887d6a28d5315f96573c47155 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Wed, 12 Aug 2020 10:12:56 -0700 Subject: [PATCH 156/736] actually use 80 for ping (mistakenly went full send to 60) --- .github/workflows/needs-more-info-closer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/needs-more-info-closer.yml b/.github/workflows/needs-more-info-closer.yml index 6cd2c6db7f7..df8324ec135 100644 --- a/.github/workflows/needs-more-info-closer.yml +++ b/.github/workflows/needs-more-info-closer.yml @@ -26,5 +26,5 @@ jobs: closeDays: 7 additionalTeam: "cleidigh|usernamehw|gjsjohnmurray|IllusionMH" closeComment: "This issue has been closed automatically because it needs more information and has not had recent activity. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" - pingDays: 60 + pingDays: 80 pingComment: "Hey @${assignee}, this issue might need further attention.\n\n@${author}, you can help us out by closing this issue if the problem no longer exists, or adding more information." From 115d7cfff59e5f27b19e47f18813a9b50f93635a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 12 Aug 2020 10:28:11 -0700 Subject: [PATCH 157/736] Allow hover x to be overridden Fixes #104058 --- src/vs/workbench/services/hover/browser/hover.ts | 6 ++++++ src/vs/workbench/services/hover/browser/hoverWidget.ts | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/hover/browser/hover.ts b/src/vs/workbench/services/hover/browser/hover.ts index a504b379d65..f696ebd64e7 100644 --- a/src/vs/workbench/services/hover/browser/hover.ts +++ b/src/vs/workbench/services/hover/browser/hover.ts @@ -109,4 +109,10 @@ export interface IHoverTarget extends IDisposable { * wrapped text. */ readonly targetElements: readonly HTMLElement[]; + + /** + * An optional absolute x coordinate to position the hover with, for example to position the + * hover using `MouseEvent.pageX`. + */ + x?: number; } diff --git a/src/vs/workbench/services/hover/browser/hoverWidget.ts b/src/vs/workbench/services/hover/browser/hoverWidget.ts index 702e4e5143b..d7135f2d671 100644 --- a/src/vs/workbench/services/hover/browser/hoverWidget.ts +++ b/src/vs/workbench/services/hover/browser/hoverWidget.ts @@ -136,9 +136,10 @@ export class HoverWidget extends Widget { this._hover.containerDomNode.classList.remove('right-aligned'); this._hover.contentsDomNode.style.maxHeight = ''; - // Get horizontal alignment and position const targetBounds = this._target.targetElements.map(e => e.getBoundingClientRect()); - const targetLeft = Math.min(...targetBounds.map(e => e.left)); + + // Get horizontal alignment and position + let targetLeft = this._target.x !== undefined ? this._target.x : Math.min(...targetBounds.map(e => e.left)); if (targetLeft + this._hover.containerDomNode.clientWidth >= document.documentElement.clientWidth) { this._x = document.documentElement.clientWidth; this._hover.containerDomNode.classList.add('right-aligned'); From 16adb5bdf1b437456d760423bf32e87a6e869127 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Wed, 12 Aug 2020 10:39:18 -0700 Subject: [PATCH 158/736] Bump actions --- .github/workflows/author-verified.yml | 2 +- .github/workflows/commands.yml | 2 +- .github/workflows/deep-classifier-monitor.yml | 2 +- .github/workflows/deep-classifier-runner.yml | 2 +- .github/workflows/deep-classifier-scraper.yml | 2 +- .github/workflows/english-please.yml | 2 +- .github/workflows/feature-request.yml | 2 +- .github/workflows/latest-release-monitor.yml | 2 +- .github/workflows/locker.yml | 2 +- .github/workflows/needs-more-info-closer.yml | 2 +- .github/workflows/on-label.yml | 2 +- .github/workflows/on-open.yml | 2 +- .github/workflows/release-pipeline-labeler.yml | 2 +- .github/workflows/test-plan-item-validator.yml | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/author-verified.yml b/.github/workflows/author-verified.yml index 7114f351353..cf67a849350 100644 --- a/.github/workflows/author-verified.yml +++ b/.github/workflows/author-verified.yml @@ -17,7 +17,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v31 + ref: v32 path: ./actions - name: Install Actions if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'author-verification-requested') diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml index 7036a4937d4..afdf723c4ed 100644 --- a/.github/workflows/commands.yml +++ b/.github/workflows/commands.yml @@ -13,7 +13,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v31 + ref: v32 - name: Install Actions run: npm install --production --prefix ./actions - name: Run Commands diff --git a/.github/workflows/deep-classifier-monitor.yml b/.github/workflows/deep-classifier-monitor.yml index 7aa6d9de22f..38c2c7df21d 100644 --- a/.github/workflows/deep-classifier-monitor.yml +++ b/.github/workflows/deep-classifier-monitor.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v31 + ref: v32 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/deep-classifier-runner.yml b/.github/workflows/deep-classifier-runner.yml index 29d7103adff..a2f86a9da0a 100644 --- a/.github/workflows/deep-classifier-runner.yml +++ b/.github/workflows/deep-classifier-runner.yml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v31 + ref: v32 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/deep-classifier-scraper.yml b/.github/workflows/deep-classifier-scraper.yml index c93f2b5352f..0f0de92ce0d 100644 --- a/.github/workflows/deep-classifier-scraper.yml +++ b/.github/workflows/deep-classifier-scraper.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v31 + ref: v32 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/english-please.yml b/.github/workflows/english-please.yml index 1ecc532ce88..6728423998e 100644 --- a/.github/workflows/english-please.yml +++ b/.github/workflows/english-please.yml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v31 + ref: v32 path: ./actions - name: Install Actions if: contains(github.event.issue.labels.*.name, '*english-please') diff --git a/.github/workflows/feature-request.yml b/.github/workflows/feature-request.yml index cdd65c77202..af3b36fa026 100644 --- a/.github/workflows/feature-request.yml +++ b/.github/workflows/feature-request.yml @@ -18,7 +18,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v31 + ref: v32 - name: Install Actions if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'feature-request') run: npm install --production --prefix ./actions diff --git a/.github/workflows/latest-release-monitor.yml b/.github/workflows/latest-release-monitor.yml index a00d3554147..99eabe33b33 100644 --- a/.github/workflows/latest-release-monitor.yml +++ b/.github/workflows/latest-release-monitor.yml @@ -14,7 +14,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v31 + ref: v32 - name: Install Actions run: npm install --production --prefix ./actions - name: Install Storage Module diff --git a/.github/workflows/locker.yml b/.github/workflows/locker.yml index d805f6a6428..7c2d8b119c6 100644 --- a/.github/workflows/locker.yml +++ b/.github/workflows/locker.yml @@ -14,7 +14,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v31 + ref: v32 - name: Install Actions run: npm install --production --prefix ./actions - name: Run Locker diff --git a/.github/workflows/needs-more-info-closer.yml b/.github/workflows/needs-more-info-closer.yml index df8324ec135..fe2f76f0e62 100644 --- a/.github/workflows/needs-more-info-closer.yml +++ b/.github/workflows/needs-more-info-closer.yml @@ -14,7 +14,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v31 + ref: v32 - name: Install Actions run: npm install --production --prefix ./actions - name: Run Needs More Info Closer diff --git a/.github/workflows/on-label.yml b/.github/workflows/on-label.yml index 97c8ee18e8e..638625f1713 100644 --- a/.github/workflows/on-label.yml +++ b/.github/workflows/on-label.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v31 + ref: v32 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/on-open.yml b/.github/workflows/on-open.yml index e8c41404d99..c02f5a9f117 100644 --- a/.github/workflows/on-open.yml +++ b/.github/workflows/on-open.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v31 + ref: v32 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/release-pipeline-labeler.yml b/.github/workflows/release-pipeline-labeler.yml index bc45221133f..fdabe0ddc07 100644 --- a/.github/workflows/release-pipeline-labeler.yml +++ b/.github/workflows/release-pipeline-labeler.yml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v31 + ref: v32 path: ./actions - name: Checkout Repo if: github.event_name != 'issues' diff --git a/.github/workflows/test-plan-item-validator.yml b/.github/workflows/test-plan-item-validator.yml index 2dbf0e45871..66beb3f1742 100644 --- a/.github/workflows/test-plan-item-validator.yml +++ b/.github/workflows/test-plan-item-validator.yml @@ -14,7 +14,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v31 + ref: v32 - name: Install Actions if: contains(github.event.issue.labels.*.name, 'testplan-item') || contains(github.event.issue.labels.*.name, 'invalid-testplan-item') run: npm install --production --prefix ./actions From fe4d4f7d027ed65cd02197e73b364548e6b6ef13 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Wed, 12 Aug 2020 10:46:21 -0700 Subject: [PATCH 159/736] Show validation message on empty fields in issue reporter, fixes #104472 --- .../issue/issueReporterMain.ts | 3 +++ .../issue/issueReporterPage.ts | 3 +++ .../issue/media/issueReporter.css | 18 +++++++++++------- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts index 1bfa1933252..fd820cf9f58 100644 --- a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts @@ -812,11 +812,14 @@ export class IssueReporter extends Disposable { private validateInput(inputId: string): boolean { const inputElement = (this.getElementById(inputId)); + const inputValidationMessage = this.getElementById(`${inputId}-empty-error`); if (!inputElement.value) { inputElement.classList.add('invalid-input'); + inputValidationMessage?.classList.remove('hidden'); return false; } else { inputElement.classList.remove('invalid-input'); + inputValidationMessage?.classList.add('hidden'); return true; } } diff --git a/src/vs/code/electron-sandbox/issue/issueReporterPage.ts b/src/vs/code/electron-sandbox/issue/issueReporterPage.ts index 5500cc46ac4..d7be46aebfa 100644 --- a/src/vs/code/electron-sandbox/issue/issueReporterPage.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterPage.ts @@ -23,6 +23,7 @@ export default (): string => ` + @@ -43,6 +44,7 @@ export default (): string => `
    + @@ -61,6 +63,7 @@ export default (): string => `
    +
    diff --git a/src/vs/code/electron-sandbox/issue/media/issueReporter.css b/src/vs/code/electron-sandbox/issue/media/issueReporter.css index e3da9bd25e4..e3c6d8f355f 100644 --- a/src/vs/code/electron-sandbox/issue/media/issueReporter.css +++ b/src/vs/code/electron-sandbox/issue/media/issueReporter.css @@ -201,9 +201,10 @@ select, input, textarea { } -.validation-error { +#issue-reporter .validation-error { font-size: 12px; - margin-top: 1em; + padding: 10px; + border-top: 0px !important; } @@ -256,8 +257,7 @@ a { } .section .input-group .validation-error { - margin-left: calc(15% + 5px); - padding: 10px; + margin-left: 100px; } .section .inline-form-control, .section .inline-label { @@ -268,7 +268,7 @@ a { width: 95px; } -.section .inline-form-control { +.section .inline-form-control, .section .input-group .validation-error { width: calc(100% - 100px); } @@ -294,9 +294,13 @@ a { margin-left: calc(15% + 1em); } - .section .inline-form-control { + .section .inline-form-control, .section .input-group .validation-error { width: calc(85% - 5px); } + + .section .input-group .validation-error { + margin-left: calc(15% + 4px); + } } @media (max-width: 620px) { @@ -308,7 +312,7 @@ a { margin-left: 1em; } - .section .inline-form-control { + .section .inline-form-control, .section .input-group .validation-error { width: 100%; } From 2c5cae3813f697afd90371ebede34dc27b7a1464 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 12 Aug 2020 10:59:50 -0700 Subject: [PATCH 160/736] Add "insert cell" submenu --- src/vs/platform/actions/common/actions.ts | 1 + .../notebook/browser/contrib/coreActions.ts | 49 +++++++++++++++---- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 36781a62572..4617ed161c1 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -120,6 +120,7 @@ export class MenuId { static readonly CommentTitle = new MenuId('CommentTitle'); static readonly CommentActions = new MenuId('CommentActions'); static readonly NotebookCellTitle = new MenuId('NotebookCellTitle'); + static readonly NotebookCellInsert = new MenuId('NotebookCellInsert'); static readonly NotebookCellBetween = new MenuId('NotebookCellBetween'); static readonly BulkEditTitle = new MenuId('BulkEditTitle'); static readonly BulkEditContext = new MenuId('BulkEditContext'); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 6fa682c7d80..c3e7e98090d 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -89,6 +89,13 @@ const enum CellToolbarOrder { DeleteCell } +const enum CellOverflowToolbarGroups { + Copy = '1_copy', + Insert = '2_insert', + Edit = '3_edit', + Collapse = '4_collapse', +} + export interface INotebookActionContext { readonly cellTemplate?: BaseCellRenderTemplate; readonly cell?: ICellViewModel; @@ -373,6 +380,12 @@ registerAction2(class extends NotebookAction { } }); +MenuRegistry.appendMenuItem(MenuId.NotebookCellTitle, { + submenu: MenuId.NotebookCellInsert, + title: localize('notebookMenu.insertCell', "Insert Cell"), + group: CellOverflowToolbarGroups.Insert +}); + MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: EXECUTE_NOTEBOOK_COMMAND_ID, @@ -409,7 +422,7 @@ registerAction2(class extends NotebookCellAction { menu: { id: MenuId.NotebookCellTitle, when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_TYPE.isEqualTo('markdown')), - group: '2_edit', + group: CellOverflowToolbarGroups.Edit, } }); } @@ -433,7 +446,7 @@ registerAction2(class extends NotebookCellAction { menu: { id: MenuId.NotebookCellTitle, when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_TYPE.isEqualTo('code')), - group: '2_edit', + group: CellOverflowToolbarGroups.Edit, } }); } @@ -526,6 +539,10 @@ registerAction2(class extends InsertCellCommand { when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, InputFocusedContext.toNegated()), weight: KeybindingWeight.WorkbenchContrib }, + menu: { + id: MenuId.NotebookCellInsert, + order: 0 + } }, CellKind.Code, 'above'); @@ -543,6 +560,10 @@ registerAction2(class extends InsertCellCommand { when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, InputFocusedContext.toNegated()), weight: KeybindingWeight.WorkbenchContrib }, + menu: { + id: MenuId.NotebookCellInsert, + order: 1 + } }, CellKind.Code, 'below'); @@ -565,6 +586,10 @@ registerAction2(class extends InsertCellCommand { { id: INSERT_MARKDOWN_CELL_ABOVE_COMMAND_ID, title: localize('notebookActions.insertMarkdownCellAbove', "Insert Markdown Cell Above"), + menu: { + id: MenuId.NotebookCellInsert, + order: 2 + } }, CellKind.Markdown, 'above'); @@ -577,6 +602,10 @@ registerAction2(class extends InsertCellCommand { { id: INSERT_MARKDOWN_CELL_BELOW_COMMAND_ID, title: localize('notebookActions.insertMarkdownCellBelow', "Insert Markdown Cell Below"), + menu: { + id: MenuId.NotebookCellInsert, + order: 3 + } }, CellKind.Markdown, 'below'); @@ -766,7 +795,7 @@ registerAction2(class extends NotebookCellAction { menu: { id: MenuId.NotebookCellTitle, when: NOTEBOOK_EDITOR_FOCUSED, - group: '1_copy', + group: CellOverflowToolbarGroups.Copy, } }); } @@ -788,7 +817,7 @@ registerAction2(class extends NotebookCellAction { menu: { id: MenuId.NotebookCellTitle, when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE), - group: '1_copy', + group: CellOverflowToolbarGroups.Copy, } }); } @@ -817,7 +846,7 @@ registerAction2(class extends NotebookAction { menu: { id: MenuId.NotebookCellTitle, when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE), - group: '1_copy', + group: CellOverflowToolbarGroups.Copy, } }); } @@ -1375,7 +1404,7 @@ registerAction2(class extends NotebookCellAction { menu: { id: MenuId.NotebookCellTitle, when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE), - group: '2_edit', + group: CellOverflowToolbarGroups.Edit, } }); } @@ -1419,7 +1448,7 @@ registerAction2(class extends NotebookCellAction { menu: { id: MenuId.NotebookCellTitle, when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_INPUT_COLLAPSED.toNegated()), - group: '3_collapse', + group: CellOverflowToolbarGroups.Collapse, } }); } @@ -1442,7 +1471,7 @@ registerAction2(class extends NotebookCellAction { menu: { id: MenuId.NotebookCellTitle, when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_INPUT_COLLAPSED), - group: '3_collapse', + group: CellOverflowToolbarGroups.Collapse, } }); } @@ -1465,7 +1494,7 @@ registerAction2(class extends NotebookCellAction { menu: { id: MenuId.NotebookCellTitle, when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_OUTPUT_COLLAPSED.toNegated(), NOTEBOOK_CELL_HAS_OUTPUTS), - group: '3_collapse', + group: CellOverflowToolbarGroups.Collapse, } }); } @@ -1488,7 +1517,7 @@ registerAction2(class extends NotebookCellAction { menu: { id: MenuId.NotebookCellTitle, when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_OUTPUT_COLLAPSED), - group: '3_collapse', + group: CellOverflowToolbarGroups.Collapse, } }); } From 5ab91a926f14298978d19122aacb07a4be7223f3 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 12 Aug 2020 11:21:52 -0700 Subject: [PATCH 161/736] fixes issue with dropdowns in lists --- src/vs/base/browser/ui/toolbar/toolbar.ts | 3 ++- src/vs/workbench/browser/parts/views/viewPaneContainer.ts | 1 + .../contrib/notebook/browser/view/renderers/cellRenderer.ts | 3 ++- src/vs/workbench/contrib/preferences/browser/settingsTree.ts | 3 ++- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 4b0908e8750..d20d6d96b53 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -26,6 +26,7 @@ export interface IToolBarOptions { actionRunner?: IActionRunner; toggleMenuTitle?: string; anchorAlignmentProvider?: () => AnchorAlignment; + renderDropdownAsChildElement?: boolean; } /** @@ -73,7 +74,7 @@ export class ToolBar extends Disposable { keybindingProvider: this.options.getKeyBinding, classNames: toolBarMoreIcon.classNames, anchorAlignmentProvider: this.options.anchorAlignmentProvider, - menuAsChild: true + menuAsChild: !!this.options.renderDropdownAsChildElement } ); this.toggleMenuActionViewItem.setActionContext(this.actionBar.context); diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index 19bc1c8badd..81a0bd9617a 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -293,6 +293,7 @@ export abstract class ViewPane extends Pane implements IView { actionViewItemProvider: action => this.getActionViewItem(action), ariaLabel: nls.localize('viewToolbarAriaLabel', "{0} actions", this.title), getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id), + renderDropdownAsChildElement: true }); this._register(this.toolbar); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index d90e00afe5f..c901747f56a 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -243,7 +243,8 @@ abstract class AbstractCellRenderer { } return undefined; - } + }, + renderDropdownAsChildElement: true }); if (elementClass) { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index a74d4d5dc25..dc5a734b6f6 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -598,7 +598,8 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre } const toolbar = new ToolBar(container, this._contextMenuService, { - toggleMenuTitle + toggleMenuTitle, + renderDropdownAsChildElement: true }); return toolbar; } From 9c1df268e3bcf4a12fe084592881825074dcc3b3 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Wed, 12 Aug 2020 11:42:08 -0700 Subject: [PATCH 162/736] Fix #104400: Maxed-out searches gives different results with context lines --- .../services/search/common/textSearchManager.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/services/search/common/textSearchManager.ts b/src/vs/workbench/services/search/common/textSearchManager.ts index 223374a7bdd..ee652bc0482 100644 --- a/src/vs/workbench/services/search/common/textSearchManager.ts +++ b/src/vs/workbench/services/search/common/textSearchManager.ts @@ -53,8 +53,7 @@ export class TextSearchManager { } const newResultSize = this.resultSize(result); - this.resultCount += newResultSize; - if (newResultSize > 0) { + this.resultCount += newResultSize; if (newResultSize > 0 || !extensionResultIsMatch(result)) { this.collector!.add(result, folderIdx); } } @@ -83,10 +82,15 @@ export class TextSearchManager { } private resultSize(result: TextSearchResult): number { - const match = result; - return Array.isArray(match.ranges) ? - match.ranges.length : - 1; + if (extensionResultIsMatch(result)) { + return Array.isArray(result.ranges) ? + result.ranges.length : + 1; + } + else { + // #104400 context lines shoudn't count towards result count + return 0; + } } private trimResultToSize(result: TextSearchMatch, size: number): TextSearchMatch { From 44dc166c1349a5c6e9ddd2b35d107b09d645c83e Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Wed, 12 Aug 2020 11:44:35 -0700 Subject: [PATCH 163/736] Formatting ref #104400 --- src/vs/workbench/services/search/common/textSearchManager.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/search/common/textSearchManager.ts b/src/vs/workbench/services/search/common/textSearchManager.ts index ee652bc0482..4913240ba05 100644 --- a/src/vs/workbench/services/search/common/textSearchManager.ts +++ b/src/vs/workbench/services/search/common/textSearchManager.ts @@ -53,7 +53,8 @@ export class TextSearchManager { } const newResultSize = this.resultSize(result); - this.resultCount += newResultSize; if (newResultSize > 0 || !extensionResultIsMatch(result)) { + this.resultCount += newResultSize; + if (newResultSize > 0 || !extensionResultIsMatch(result)) { this.collector!.add(result, folderIdx); } } From 01bf31e1bf85b4433f013ee9a93d60f5129e305d Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Wed, 12 Aug 2020 11:44:59 -0700 Subject: [PATCH 164/736] Make accounts toggle consistent with other menu items, fixes #104512 --- src/vs/workbench/browser/parts/activitybar/activitybarPart.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 1d8b7d7999d..d2f8b902d61 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -168,13 +168,12 @@ export class ActivitybarPart extends Part implements IActivityBarService { const toggleAccountsVisibilityAction = new Action( 'toggleAccountsVisibility', - nls.localize('accounts', "Accounts"), + this.accountsVisibilityPreference ? nls.localize('hideAccounts', "Hide Accounts") : nls.localize('showAccounts', "Show Accounts"), undefined, true, async () => { this.accountsVisibilityPreference = !this.accountsVisibilityPreference; } ); - toggleAccountsVisibilityAction.checked = !!this.accountsActivityAction; actions.push(toggleAccountsVisibilityAction); actions.push(new Separator()); From 0f477731e671fabff655ce6d7a06575d2368e12f Mon Sep 17 00:00:00 2001 From: bamurtaugh Date: Wed, 12 Aug 2020 11:50:42 -0700 Subject: [PATCH 165/736] Use dependency injection --- src/vs/workbench/contrib/debug/browser/debugService.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 67b93cf2ce6..07545ff4a2e 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -46,7 +46,7 @@ import { generateUuid } from 'vs/base/common/uuid'; import { DebugStorage } from 'vs/workbench/contrib/debug/common/debugStorage'; import { DebugTelemetry } from 'vs/workbench/contrib/debug/common/debugTelemetry'; import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompoundRoot'; -import { CommandService } from 'vs/workbench/services/commands/common/commandService'; +import { ICommandService } from 'vs/platform/commands/common/commands'; export class DebugService implements IDebugService { declare readonly _serviceBrand: undefined; @@ -72,7 +72,6 @@ export class DebugService implements IDebugService { private previousState: State | undefined; private sessionCancellationTokens = new Map(); private activity: IDisposable | undefined; - private commandService!: CommandService; constructor( @IEditorService private readonly editorService: IEditorService, @@ -89,7 +88,8 @@ export class DebugService implements IDebugService { @IFileService private readonly fileService: IFileService, @IConfigurationService private readonly configurationService: IConfigurationService, @IExtensionHostDebugService private readonly extensionHostDebugService: IExtensionHostDebugService, - @IActivityService private readonly activityService: IActivityService + @IActivityService private readonly activityService: IActivityService, + @ICommandService private readonly commandService: ICommandService ) { this.toDispose = []; @@ -120,7 +120,6 @@ export class DebugService implements IDebugService { this.viewModel = new ViewModel(contextKeyService); this.taskRunner = this.instantiationService.createInstance(DebugTaskRunner); - this.commandService = this.instantiationService.createInstance(CommandService); this.toDispose.push(this.fileService.onDidFilesChange(e => this.onFileChanges(e))); this.toDispose.push(this.lifecycleService.onShutdown(this.dispose, this)); From ff5a252d06325f1ac6f04b2d7a66839360adfe4b Mon Sep 17 00:00:00 2001 From: chrisdias Date: Wed, 12 Aug 2020 12:28:41 -0700 Subject: [PATCH 166/736] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 302de8a74d2..e40c9e33ec3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.49.0", - "distro": "d368861bfb088f58e20fd0ebbcaeac0104e81f80", + "distro": "af085a7e151908a14d22a08a22374f924b8a593d", "author": { "name": "Microsoft Corporation" }, From e4a77a8009e0b6e77848f3c8adc0574a96a04153 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Wed, 12 Aug 2020 12:39:05 -0700 Subject: [PATCH 167/736] Fix #100328: search view: preserve last focused item --- .../workbench/contrib/search/browser/searchView.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index ad1ba249fe8..96521c068ed 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -110,6 +110,7 @@ export class SearchView extends ViewPane { private folderMatchFocused: IContextKey; private matchFocused: IContextKey; private hasSearchResultsKey: IContextKey; + private lastFocusState: 'input' | 'tree' = 'input'; private state: SearchUIState = SearchUIState.Idle; @@ -376,6 +377,9 @@ export class SearchView extends ViewPane { this.updateActions(); this.updatedActionsWhileHidden = false; } + } else { + // Reset last focus to input to preserve opening the viewlet always focusing the query editor. + this.lastFocusState = 'input'; } // Enable highlights if there are searchresults @@ -476,6 +480,7 @@ export class SearchView extends ViewPane { private trackInputBox(inputFocusTracker: dom.IFocusTracker, contextKey?: IContextKey): void { this._register(inputFocusTracker.onDidFocus(() => { + this.lastFocusState = 'input'; this.inputBoxFocused.set(true); if (contextKey) { contextKey.set(true); @@ -751,6 +756,7 @@ export class SearchView extends ViewPane { this.matchFocused.set(focus instanceof Match); this.fileMatchOrFolderMatchFocus.set(focus instanceof FileMatch || focus instanceof FolderMatch); this.fileMatchOrFolderMatchWithResourceFocus.set(focus instanceof FileMatch || focus instanceof FolderMatchWithResource); + this.lastFocusState = 'tree'; } })); @@ -874,8 +880,12 @@ export class SearchView extends ViewPane { focus(): void { super.focus(); - const updatedText = this.searchConfig.seedOnFocus ? this.updateTextFromSelection({ allowSearchOnType: false }) : false; - this.searchWidget.focus(undefined, undefined, updatedText); + if (this.lastFocusState === 'input' || !this.hasSearchResults()) { + const updatedText = this.searchConfig.seedOnFocus ? this.updateTextFromSelection({ allowSearchOnType: false }) : false; + this.searchWidget.focus(undefined, undefined, updatedText); + } else { + this.tree.domFocus(); + } } updateTextFromSelection({ allowUnselectedWord = true, allowSearchOnType = true }): boolean { From 7d9f54c96ed2b1b871dac113bf8d2854612b2aa6 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 12 Aug 2020 11:53:01 -0700 Subject: [PATCH 168/736] add join cell above into toolbar context menu --- .../contrib/notebook/browser/contrib/coreActions.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index c3e7e98090d..6970f321a6b 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -1381,6 +1381,11 @@ registerAction2(class extends NotebookCellAction { when: NOTEBOOK_EDITOR_FOCUSED, primary: KeyMod.WinCtrl | KeyMod.Alt | KeyMod.Shift | KeyCode.KEY_J, weight: KeybindingWeight.WorkbenchContrib + }, + menu: { + id: MenuId.NotebookCellTitle, + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE), + group: '2_edit', } }); } From 37697bc55639a4a8571237f4c7a5aabda6173d13 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 12 Aug 2020 13:24:25 -0700 Subject: [PATCH 169/736] fix #104128. --- .../contrib/notebook/browser/contrib/coreActions.ts | 6 +++--- .../contrib/notebook/browser/notebookBrowser.ts | 2 ++ .../notebook/browser/notebookEditorWidget.ts | 7 ++++++- .../browser/view/renderers/cellContextKeys.ts | 13 ++++++++++++- .../notebook/browser/view/renderers/cellRenderer.ts | 4 ++-- .../contrib/notebook/test/testNotebookEditor.ts | 2 +- 6 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 6970f321a6b..1f030d65559 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -18,7 +18,7 @@ import { InputFocusedContext, InputFocusedContextKey } from 'vs/platform/context import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; -import { BaseCellRenderTemplate, CellEditState, CellFocusMode, ICellViewModel, INotebookEditor, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_OUTPUT_FOCUSED, EXPAND_CELL_CONTENT_COMMAND_ID } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { BaseCellRenderTemplate, CellEditState, CellFocusMode, ICellViewModel, INotebookEditor, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_OUTPUT_FOCUSED, EXPAND_CELL_CONTENT_COMMAND_ID, NOTEBOOK_CELL_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { CellKind, CellUri, NotebookCellRunState, NOTEBOOK_EDITOR_CURSOR_BOUNDARY } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; @@ -1178,7 +1178,7 @@ registerAction2(class extends NotebookCellAction { title: localize('clearActiveCellOutputs', 'Clear Active Cell Outputs'), menu: { id: MenuId.NotebookCellTitle, - when: ContextKeyExpr.and(NOTEBOOK_CELL_TYPE.isEqualTo('code'), NOTEBOOK_EDITOR_RUNNABLE), + when: ContextKeyExpr.and(NOTEBOOK_CELL_TYPE.isEqualTo('code'), NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_CELL_HAS_OUTPUTS), order: CellToolbarOrder.ClearCellOutput, group: CELL_TITLE_OUTPUT_GROUP_ID }, @@ -1341,7 +1341,7 @@ registerAction2(class extends NotebookCellAction { title: localize('notebookActions.splitCell', "Split Cell"), menu: { id: MenuId.NotebookCellTitle, - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, InputFocusedContext), + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_FOCUSED, InputFocusedContext), order: CellToolbarOrder.SplitCell, group: CELL_TITLE_CELL_GROUP_ID, // alt: { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 8f14fcaddf0..878152d3ffe 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -45,6 +45,7 @@ export const NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK = new RawContextKey('no export const NOTEBOOK_VIEW_TYPE = new RawContextKey('notebookViewType', undefined); export const NOTEBOOK_CELL_TYPE = new RawContextKey('notebookCellType', undefined); // code, markdown export const NOTEBOOK_CELL_EDITABLE = new RawContextKey('notebookCellEditable', false); // bool +export const NOTEBOOK_CELL_FOCUSED = new RawContextKey('notebookCellFocused', false); // bool export const NOTEBOOK_CELL_RUNNABLE = new RawContextKey('notebookCellRunnable', false); // bool export const NOTEBOOK_CELL_MARKDOWN_EDIT_MODE = new RawContextKey('notebookCellMarkdownEditMode', false); // bool export const NOTEBOOK_CELL_RUN_STATE = new RawContextKey('notebookCellRunState', undefined); // idle, running @@ -188,6 +189,7 @@ export interface INotebookEditor extends IEditor { multipleKernelsAvailable: boolean; readonly onDidChangeAvailableKernels: Event; readonly onDidChangeKernel: Event; + readonly onDidChangeActiveCell: Event; isDisposed: boolean; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index f1894d442ae..6ae7a4abea0 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -192,6 +192,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return this._renderedEditors.get(focused); } + private readonly _onDidChangeActiveCell = this._register(new Emitter()); + readonly onDidChangeActiveCell: Event = this._onDidChangeActiveCell.event; + + private _cursorNavigationMode: boolean = false; get cursorNavigationMode(): boolean { return this._cursorNavigationMode; @@ -460,6 +464,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._register(this._list.onDidChangeFocus(_e => { this._onDidChangeActiveEditor.fire(this); + this._onDidChangeActiveCell.fire(); this._cursorNavigationMode = false; })); @@ -556,7 +561,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor const focused = this._list!.getFocusedElements()[0]; if (focused) { if (!this._cellContextKeyManager) { - this._cellContextKeyManager = this._localStore.add(new CellContextKeyManager(this.contextKeyService, textModel, focused as CellViewModel)); + this._cellContextKeyManager = this._localStore.add(new CellContextKeyManager(this.contextKeyService, this, textModel, focused as CellViewModel)); } this._cellContextKeyManager.updateForElement(focused as CellViewModel); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys.ts index 3c3b9eac7f4..1060d348525 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys.ts @@ -6,7 +6,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { INotebookTextModel, NotebookCellRunState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel'; -import { NOTEBOOK_CELL_TYPE, NOTEBOOK_VIEW_TYPE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_RUNNABLE, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_RUN_STATE, NOTEBOOK_CELL_HAS_OUTPUTS, CellViewModelStateChangeEvent, CellEditState, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_OUTPUT_COLLAPSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { NOTEBOOK_CELL_TYPE, NOTEBOOK_VIEW_TYPE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_RUNNABLE, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_RUN_STATE, NOTEBOOK_CELL_HAS_OUTPUTS, CellViewModelStateChangeEvent, CellEditState, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_FOCUSED, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; @@ -17,6 +17,7 @@ export class CellContextKeyManager extends Disposable { private viewType!: IContextKey; private cellEditable!: IContextKey; private cellRunnable!: IContextKey; + private cellFocused!: IContextKey; private cellRunState!: IContextKey; private cellHasOutputs!: IContextKey; private cellContentCollapsed!: IContextKey; @@ -28,6 +29,7 @@ export class CellContextKeyManager extends Disposable { constructor( private readonly contextKeyService: IContextKeyService, + private readonly notebookEditor: INotebookEditor, private readonly notebookTextModel: INotebookTextModel, private element: BaseCellViewModel ) { @@ -37,6 +39,7 @@ export class CellContextKeyManager extends Disposable { this.cellType = NOTEBOOK_CELL_TYPE.bindTo(this.contextKeyService); this.viewType = NOTEBOOK_VIEW_TYPE.bindTo(this.contextKeyService); this.cellEditable = NOTEBOOK_CELL_EDITABLE.bindTo(this.contextKeyService); + this.cellFocused = NOTEBOOK_CELL_FOCUSED.bindTo(this.contextKeyService); this.cellRunnable = NOTEBOOK_CELL_RUNNABLE.bindTo(this.contextKeyService); this.markdownEditMode = NOTEBOOK_CELL_MARKDOWN_EDIT_MODE.bindTo(this.contextKeyService); this.cellRunState = NOTEBOOK_CELL_RUN_STATE.bindTo(this.contextKeyService); @@ -57,6 +60,7 @@ export class CellContextKeyManager extends Disposable { } this.elementDisposables.add(element.model.onDidChangeMetadata(() => this.updateForCollapseState())); + this.elementDisposables.add(this.notebookEditor.onDidChangeActiveCell(() => this.updateForFocusState())); this.element = element; if (this.element instanceof MarkdownCellViewModel) { @@ -66,6 +70,7 @@ export class CellContextKeyManager extends Disposable { } this.contextKeyService.bufferChangeEvents(() => { + this.updateForFocusState(); this.updateForMetadata(); this.updateForEditState(); this.updateForCollapseState(); @@ -91,6 +96,10 @@ export class CellContextKeyManager extends Disposable { }); } + private updateForFocusState() { + this.cellFocused.set(this.notebookEditor.getActiveCell() === this.element); + } + private updateForMetadata() { const metadata = this.element.getEvaluatedMetadata(this.notebookTextModel.metadata); this.cellEditable.set(!!metadata.editable); @@ -115,8 +124,10 @@ export class CellContextKeyManager extends Disposable { private updateForOutputs() { if (this.element instanceof CodeCellViewModel) { + console.log(this.element, this.element.outputs.length > 0); this.cellHasOutputs.set(this.element.outputs.length > 0); } else { + console.log(this.element, false); this.cellHasOutputs.set(false); } } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index c901747f56a..c36c13e1ad6 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -480,7 +480,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR const elementDisposables = templateData.elementDisposables; - elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor.viewModel?.notebookDocument!, element)); + elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor, this.notebookEditor.viewModel?.notebookDocument!, element)); // render toolbar first this.setupCellToolbarActions(templateData, elementDisposables); @@ -1147,7 +1147,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende elementDisposables.add(this.instantiationService.createInstance(CodeCell, this.notebookEditor, element, templateData)); this.renderedEditors.set(element, templateData.editor); - elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor.viewModel?.notebookDocument!, element)); + elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor, this.notebookEditor.viewModel?.notebookDocument!, element)); this.updateForLayout(element, templateData); elementDisposables.add(element.onDidChangeLayout(() => { diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index bee4079ebd8..71fcf82ed16 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -68,7 +68,7 @@ export class TestNotebookEditor implements INotebookEditor { multipleKernelsAvailable: boolean = false; onDidChangeAvailableKernels: Event = new Emitter().event; - + onDidChangeActiveCell: Event = new Emitter().event; uri?: URI | undefined; textModel?: NotebookTextModel | undefined; From 8c1aa16f4b74ee243be4da0b7a3fb09cfc70b82c Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 12 Aug 2020 13:34:15 -0700 Subject: [PATCH 170/736] fix #102156. --- .../notebook/browser/notebookServiceImpl.ts | 72 +++++++++++++------ 1 file changed, 51 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 24821e8fbe3..99c441658f7 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -389,7 +389,7 @@ export class NotebookService extends Disposable implements INotebookService, ICu } const { editor, activeCell } = getContext(); - if (!editor || !activeCell) { + if (!editor) { return false; } @@ -403,32 +403,62 @@ export class NotebookService extends Disposable implements INotebookService, ICu return false; } - const currCellIndex = viewModel.getCellIndex(activeCell); + if (activeCell) { + const currCellIndex = viewModel.getCellIndex(activeCell); - let topPastedCell: CellViewModel | undefined = undefined; - pasteCells.items.reverse().map(cell => { - const data = CellUri.parse(cell.uri); + let topPastedCell: CellViewModel | undefined = undefined; + pasteCells.items.reverse().map(cell => { + const data = CellUri.parse(cell.uri); - if (pasteCells.isCopy || data?.notebook.toString() !== viewModel.uri.toString()) { - return viewModel.notebookDocument.createCellTextModel( - cell.getValue(), - cell.language, - cell.cellKind, - [], - cell.metadata - ); - } else { - return cell; + if (pasteCells.isCopy || data?.notebook.toString() !== viewModel.uri.toString()) { + return viewModel.notebookDocument.createCellTextModel( + cell.getValue(), + cell.language, + cell.cellKind, + [], + cell.metadata + ); + } else { + return cell; + } + }).forEach(pasteCell => { + const newIdx = typeof currCellIndex === 'number' ? currCellIndex + 1 : 0; + topPastedCell = viewModel.insertCell(newIdx, pasteCell, true); + }); + + if (topPastedCell) { + editor.focusNotebookCell(topPastedCell, 'container'); + } + } else { + if (viewModel.length !== 0) { + return false; } - }).forEach(pasteCell => { - const newIdx = typeof currCellIndex === 'number' ? currCellIndex + 1 : 0; - topPastedCell = viewModel.insertCell(newIdx, pasteCell, true); - }); - if (topPastedCell) { - editor.focusNotebookCell(topPastedCell, 'container'); + let topPastedCell: CellViewModel | undefined = undefined; + pasteCells.items.reverse().map(cell => { + const data = CellUri.parse(cell.uri); + + if (pasteCells.isCopy || data?.notebook.toString() !== viewModel.uri.toString()) { + return viewModel.notebookDocument.createCellTextModel( + cell.getValue(), + cell.language, + cell.cellKind, + [], + cell.metadata + ); + } else { + return cell; + } + }).forEach(pasteCell => { + topPastedCell = viewModel.insertCell(0, pasteCell, true); + }); + + if (topPastedCell) { + editor.focusNotebookCell(topPastedCell, 'container'); + } } + return true; }); } From 4a67f133c87200b68108ea6ed3ea5c28bfa17147 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Tue, 26 Nov 2019 23:44:21 -0500 Subject: [PATCH 171/736] Removes short-circuit that causes mis-alignment --- src/vs/workbench/contrib/views/browser/treeView.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/vs/workbench/contrib/views/browser/treeView.ts b/src/vs/workbench/contrib/views/browser/treeView.ts index b13c36fadfb..0e76fd613c4 100644 --- a/src/vs/workbench/contrib/views/browser/treeView.ts +++ b/src/vs/workbench/contrib/views/browser/treeView.ts @@ -900,9 +900,6 @@ class Aligner extends Disposable { if (this._tree) { const parent: ITreeItem = this._tree.getParentElement(treeItem) || this._tree.getInput(); - if (this.hasIcon(parent)) { - return false; - } return !!parent.children && parent.children.every(c => c.collapsibleState === TreeItemCollapsibleState.None || !this.hasIcon(c)); } else { return false; From 7c48b0cfa22127c4051e3a0b46b419347aae8fb7 Mon Sep 17 00:00:00 2001 From: noecald Date: Wed, 12 Aug 2020 13:42:33 -0700 Subject: [PATCH 172/736] removed 2.2 dependency --- .github/workflows/rich-navigation.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/rich-navigation.yml b/.github/workflows/rich-navigation.yml index 185c770dd0f..ca1d785a6d6 100644 --- a/.github/workflows/rich-navigation.yml +++ b/.github/workflows/rich-navigation.yml @@ -16,10 +16,6 @@ jobs: run: yarn --frozen-lockfile env: CHILD_CONCURRENCY: 1 - - name: Install .NET Core 2.2 - uses: actions/setup-dotnet@v1.5.0 - with: - dotnet-version: 2.2 - uses: microsoft/RichCodeNavIndexer@v0.1 with: languages: typescript From 4d77523642d1529d7c3669773f4413f132b5d1f6 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 12 Aug 2020 13:45:59 -0700 Subject: [PATCH 173/736] Add context menu to notebook cells Also make inline actions always show up in the overflow menu as well Fix #103920 --- .../notebook/browser/contrib/coreActions.ts | 15 ++++++--- .../notebook/browser/notebookBrowser.ts | 3 +- .../notebook/browser/notebookEditorWidget.ts | 32 ++++++++++++++++++- .../browser/view/renderers/cellActionView.ts | 13 +++++--- .../browser/view/renderers/cellRenderer.ts | 8 ++--- 5 files changed, 56 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 1f030d65559..92627f195c8 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -386,6 +386,13 @@ MenuRegistry.appendMenuItem(MenuId.NotebookCellTitle, { group: CellOverflowToolbarGroups.Insert }); +MenuRegistry.appendMenuItem(MenuId.EditorContext, { + submenu: MenuId.NotebookCellTitle, + title: localize('notebookMenu.cellTitle', "Notebook Cell"), + group: CellOverflowToolbarGroups.Insert, + when: NOTEBOOK_EDITOR_FOCUSED +}); + MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: EXECUTE_NOTEBOOK_COMMAND_ID, @@ -1452,7 +1459,7 @@ registerAction2(class extends NotebookCellAction { }, menu: { id: MenuId.NotebookCellTitle, - when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_INPUT_COLLAPSED.toNegated()), + when: ContextKeyExpr.and(NOTEBOOK_CELL_INPUT_COLLAPSED.toNegated()), group: CellOverflowToolbarGroups.Collapse, } }); @@ -1475,7 +1482,7 @@ registerAction2(class extends NotebookCellAction { }, menu: { id: MenuId.NotebookCellTitle, - when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_INPUT_COLLAPSED), + when: ContextKeyExpr.and(NOTEBOOK_CELL_INPUT_COLLAPSED), group: CellOverflowToolbarGroups.Collapse, } }); @@ -1498,7 +1505,7 @@ registerAction2(class extends NotebookCellAction { }, menu: { id: MenuId.NotebookCellTitle, - when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_OUTPUT_COLLAPSED.toNegated(), NOTEBOOK_CELL_HAS_OUTPUTS), + when: ContextKeyExpr.and(NOTEBOOK_CELL_OUTPUT_COLLAPSED.toNegated(), NOTEBOOK_CELL_HAS_OUTPUTS), group: CellOverflowToolbarGroups.Collapse, } }); @@ -1521,7 +1528,7 @@ registerAction2(class extends NotebookCellAction { }, menu: { id: MenuId.NotebookCellTitle, - when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_OUTPUT_COLLAPSED), + when: ContextKeyExpr.and(NOTEBOOK_CELL_OUTPUT_COLLAPSED), group: CellOverflowToolbarGroups.Collapse, } }); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 878152d3ffe..9cd6ec918a8 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; -import { IListEvent, IListMouseEvent } from 'vs/base/browser/ui/list/list'; +import { IListContextMenuEvent, IListEvent, IListMouseEvent } from 'vs/base/browser/ui/list/list'; import { IListOptions, IListStyles } from 'vs/base/browser/ui/list/listWidget'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; @@ -435,6 +435,7 @@ export interface INotebookCellList { readonly onDidHideOutput: Event; readonly onMouseUp: Event>; readonly onMouseDown: Event>; + readonly onContextMenu: Event>; detachViewModel(): void; attachViewModel(viewModel: NotebookViewModel): void; clear(): void; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 6ae7a4abea0..b8487f2b538 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -51,6 +51,10 @@ import { debugIconStartForeground } from 'vs/workbench/contrib/debug/browser/deb import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys'; import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { notebookKernelProviderAssociationsSettingId, NotebookKernelProviderAssociations } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation'; +import { IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { IAction, Separator } from 'vs/base/common/actions'; const $ = DOM.$; @@ -211,7 +215,9 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor @INotebookService private notebookService: INotebookService, @IConfigurationService private readonly configurationService: IConfigurationService, @IContextKeyService readonly contextKeyService: IContextKeyService, - @ILayoutService private readonly layoutService: ILayoutService + @ILayoutService private readonly layoutService: ILayoutService, + @IContextMenuService private readonly contextMenuService: IContextMenuService, + @IMenuService private readonly menuService: IMenuService, ) { super(); this._memento = new Memento(NotebookEditorWidget.ID, storageService); @@ -468,10 +474,34 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._cursorNavigationMode = false; })); + this._register(this._list.onContextMenu(e => { + this.showListContextMenu(e); + })); + const widgetFocusTracker = DOM.trackFocus(this.getDomNode()); this._register(widgetFocusTracker); this._register(widgetFocusTracker.onDidFocus(() => this._onDidFocusEmitter.fire())); + } + private showListContextMenu(e: IListContextMenuEvent) { + this.contextMenuService.showContextMenu({ + getActions: () => { + const result: IAction[] = []; + const menu = this.menuService.createMenu(MenuId.NotebookCellTitle, this.contextKeyService); + const groups = menu.getActions(); + menu.dispose(); + + for (let group of groups) { + const [, actions] = group; + result.push(...actions); + result.push(new Separator()); + } + + result.pop(); // remove last separator + return result; + }, + getAnchor: () => e.anchor + }); } private _updateForCursorNavigationMode(applyFocusChange: () => void): void { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts index c5ccf32bc91..69e4ddad204 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts @@ -29,21 +29,22 @@ export class VerticalSeparatorViewItem extends BaseActionViewItem { } } -export function createAndFillInActionBarActionsWithVerticalSeparators(menu: IMenu, options: IMenuActionOptions | undefined, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, isPrimaryGroup?: (group: string) => boolean): IDisposable { +export function createAndFillInActionBarActionsWithVerticalSeparators(menu: IMenu, options: IMenuActionOptions | undefined, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, alwaysFillSecondary?: boolean, isPrimaryGroup?: (group: string) => boolean): IDisposable { const groups = menu.getActions(options); // Action bars handle alternative actions on their own so the alternative actions should be ignored - fillInActions(groups, target, false, isPrimaryGroup); + fillInActions(groups, target, false, alwaysFillSecondary, isPrimaryGroup); return asDisposable(groups); } -function fillInActions(groups: ReadonlyArray<[string, ReadonlyArray]>, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, useAlternativeActions: boolean, isPrimaryGroup: (group: string) => boolean = group => group === 'navigation'): void { +function fillInActions(groups: ReadonlyArray<[string, ReadonlyArray]>, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, useAlternativeActions: boolean, alwaysFillSecondary = false, isPrimaryGroup: (group: string) => boolean = group => group === 'navigation'): void { for (const tuple of groups) { let [group, actions] = tuple; if (useAlternativeActions) { actions = actions.map(a => (a instanceof MenuItemAction) && !!a.alt ? a.alt : a); } - if (isPrimaryGroup(group)) { + const isPrimary = isPrimaryGroup(group); + if (isPrimary) { const to = Array.isArray(target) ? target : target.primary; if (to.length > 0) { @@ -51,7 +52,9 @@ function fillInActions(groups: ReadonlyArray<[string, ReadonlyArray(target) ? target : target.secondary; if (to.length > 0) { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index c36c13e1ad6..d438c86d154 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -209,7 +209,7 @@ abstract class AbstractCellRenderer { const cellMenu = this.instantiationService.createInstance(CellMenus); const menu = disposables.add(cellMenu.getCellInsertionMenu(contextKeyService)); - const actions = this.getCellToolbarActions(menu); + const actions = this.getCellToolbarActions(menu, false); toolbar.setActions(actions.primary, actions.secondary); return toolbar; @@ -254,19 +254,19 @@ abstract class AbstractCellRenderer { return toolbar; } - private getCellToolbarActions(menu: IMenu): { primary: IAction[], secondary: IAction[] } { + private getCellToolbarActions(menu: IMenu, alwaysFillSecondaryActions: boolean): { primary: IAction[], secondary: IAction[] } { const primary: IAction[] = []; const secondary: IAction[] = []; const result = { primary, secondary }; - createAndFillInActionBarActionsWithVerticalSeparators(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g)); + createAndFillInActionBarActionsWithVerticalSeparators(menu, { shouldForwardArgs: true }, result, alwaysFillSecondaryActions, g => /^inline/.test(g)); return result; } protected setupCellToolbarActions(templateData: BaseCellRenderTemplate, disposables: DisposableStore): void { const updateActions = () => { - const actions = this.getCellToolbarActions(templateData.titleMenu); + const actions = this.getCellToolbarActions(templateData.titleMenu, true); const hadFocus = DOM.isAncestor(document.activeElement, templateData.toolbar.getElement()); templateData.toolbar.setActions(actions.primary, actions.secondary); From edcc70c8a3c2995c49993612e840541f9cb4f821 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 12 Aug 2020 13:48:53 -0700 Subject: [PATCH 174/736] :lipstick: --- .../contrib/notebook/browser/view/renderers/cellContextKeys.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys.ts index 1060d348525..945ba2e54c8 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys.ts @@ -124,10 +124,8 @@ export class CellContextKeyManager extends Disposable { private updateForOutputs() { if (this.element instanceof CodeCellViewModel) { - console.log(this.element, this.element.outputs.length > 0); this.cellHasOutputs.set(this.element.outputs.length > 0); } else { - console.log(this.element, false); this.cellHasOutputs.set(false); } } From 467f014634672ec79d053d4fd397dcc01bc6322a Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Wed, 12 Aug 2020 16:57:34 -0400 Subject: [PATCH 175/736] Removes rogue onFileSystem activation --- extensions/vscode-web-playground/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/vscode-web-playground/package.json b/extensions/vscode-web-playground/package.json index 954aec0fae9..67ebd70edd4 100644 --- a/extensions/vscode-web-playground/package.json +++ b/extensions/vscode-web-playground/package.json @@ -8,7 +8,6 @@ "private": true, "activationEvents": [ "onFileSystem:memfs", - "onFileSystem:github", "onDebug" ], "browser": "./dist/browser/extension", From cb633a5d73f8babd89800cf8c7a79fc0444b8dca Mon Sep 17 00:00:00 2001 From: noecald Date: Wed, 12 Aug 2020 14:05:27 -0700 Subject: [PATCH 176/736] add minor version --- .github/workflows/rich-navigation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rich-navigation.yml b/.github/workflows/rich-navigation.yml index ca1d785a6d6..432987f531f 100644 --- a/.github/workflows/rich-navigation.yml +++ b/.github/workflows/rich-navigation.yml @@ -16,7 +16,7 @@ jobs: run: yarn --frozen-lockfile env: CHILD_CONCURRENCY: 1 - - uses: microsoft/RichCodeNavIndexer@v0.1 + - uses: microsoft/RichCodeNavIndexer@v0.1.3 with: languages: typescript repo-token: ${{ secrets.GITHUB_TOKEN }} From 3e9c6eafd69d16a2d064bf5939d985ab87d1a7e3 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 12 Aug 2020 14:44:06 -0700 Subject: [PATCH 177/736] debug: fix parent session badge not hiding correctly Fixes https://github.com/microsoft/vscode-js-debug/issues/664 --- src/vs/workbench/contrib/debug/browser/callStackView.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 1d4f3bf717d..1153b13d0a3 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -545,7 +545,7 @@ class SessionsRenderer implements ICompressibleTreeRenderer setActionBar())); - data.stateLabel.hidden = false; + data.stateLabel.style.display = ''; if (thread && thread.stoppedDetails) { data.stateLabel.textContent = thread.stoppedDetails.description || nls.localize('debugStopped', "Paused on {0}", thread.stoppedDetails.reason || ''); @@ -557,7 +557,7 @@ class SessionsRenderer implements ICompressibleTreeRenderer Date: Wed, 12 Aug 2020 18:11:31 -0400 Subject: [PATCH 178/736] Better alignment fix of items w/ icons Items with an icon that belong to a parent with an icon, but have siblings with children but without icons should now be aligned properly --- src/vs/workbench/contrib/views/browser/treeView.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/contrib/views/browser/treeView.ts b/src/vs/workbench/contrib/views/browser/treeView.ts index 0e76fd613c4..66a89b23bec 100644 --- a/src/vs/workbench/contrib/views/browser/treeView.ts +++ b/src/vs/workbench/contrib/views/browser/treeView.ts @@ -900,6 +900,9 @@ class Aligner extends Disposable { if (this._tree) { const parent: ITreeItem = this._tree.getParentElement(treeItem) || this._tree.getInput(); + if (this.hasIcon(parent)) { + return !!parent.children && parent.children.some(c => c.collapsibleState !== TreeItemCollapsibleState.None && !this.hasIcon(c)); + } return !!parent.children && parent.children.every(c => c.collapsibleState === TreeItemCollapsibleState.None || !this.hasIcon(c)); } else { return false; From b51f344771f159ea5ffd7e37948aadbae9014d76 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 12 Aug 2020 11:51:36 -0700 Subject: [PATCH 179/736] don't use tranlate3d in notebooks for custom menus Co-authored-by: SteVen Batten Co-authored-by: rebornix --- .../contrib/notebook/browser/notebookEditorWidget.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index b8487f2b538..05575ea7039 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -55,6 +55,9 @@ import { IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IAction, Separator } from 'vs/base/common/actions'; +import { isMacintosh, isNative } from 'vs/base/common/platform'; +import { getTitleBarStyle } from 'vs/platform/windows/common/windows'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; const $ = DOM.$; @@ -214,6 +217,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor @IStorageService storageService: IStorageService, @INotebookService private notebookService: INotebookService, @IConfigurationService private readonly configurationService: IConfigurationService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, @IContextKeyService readonly contextKeyService: IContextKeyService, @ILayoutService private readonly layoutService: ILayoutService, @IContextMenuService private readonly contextMenuService: IContextMenuService, @@ -401,7 +405,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor multipleSelectionSupport: false, enableKeyboardNavigation: true, additionalScrollHeight: 0, - transformOptimization: true, + transformOptimization: (isMacintosh && isNative) || getTitleBarStyle(this.configurationService, this.environmentService) === 'native', styleController: (_suffix: string) => { return this._list!; }, overrideStyles: { listBackground: editorBackground, From eeae2a6445f58123b1591ece6596d34aaf94cd35 Mon Sep 17 00:00:00 2001 From: noecald Date: Wed, 12 Aug 2020 15:39:25 -0700 Subject: [PATCH 180/736] revert to 2.2 and 0.1.2 temporarily --- .github/workflows/rich-navigation.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rich-navigation.yml b/.github/workflows/rich-navigation.yml index 432987f531f..cfc0381eead 100644 --- a/.github/workflows/rich-navigation.yml +++ b/.github/workflows/rich-navigation.yml @@ -16,7 +16,11 @@ jobs: run: yarn --frozen-lockfile env: CHILD_CONCURRENCY: 1 - - uses: microsoft/RichCodeNavIndexer@v0.1.3 + - name: Install .NET Core 2.2 + uses: actions/setup-dotnet@v1.5.0 + with: + dotnet-version: 2.2 + - uses: microsoft/RichCodeNavIndexer@v0.1.2 with: languages: typescript repo-token: ${{ secrets.GITHUB_TOKEN }} From bf5bb7fc61b3f139ac26e2c0292ee39922d7c01f Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 12 Aug 2020 16:07:04 -0700 Subject: [PATCH 181/736] push SCM above added panes by default --- src/vs/workbench/contrib/scm/browser/scm.contribution.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index f5a22758da5..bf905250784 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -62,6 +62,7 @@ viewsRegistry.registerViews([{ workspace: true, canMoveView: true, weight: 80, + order: -999, containerIcon: Codicon.sourceControl.classNames }], viewContainer); From c80b8a3318e6ea354061be254a69f148c2c500b2 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 12 Aug 2020 16:18:24 -0700 Subject: [PATCH 182/736] Fix wrapping overflow when up against the edge Fixes #104508 --- src/vs/workbench/services/hover/browser/hoverWidget.ts | 2 +- src/vs/workbench/services/hover/browser/media/hover.css | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/hover/browser/hoverWidget.ts b/src/vs/workbench/services/hover/browser/hoverWidget.ts index d7135f2d671..84e12a399ba 100644 --- a/src/vs/workbench/services/hover/browser/hoverWidget.ts +++ b/src/vs/workbench/services/hover/browser/hoverWidget.ts @@ -141,7 +141,7 @@ export class HoverWidget extends Widget { // Get horizontal alignment and position let targetLeft = this._target.x !== undefined ? this._target.x : Math.min(...targetBounds.map(e => e.left)); if (targetLeft + this._hover.containerDomNode.clientWidth >= document.documentElement.clientWidth) { - this._x = document.documentElement.clientWidth; + this._x = document.documentElement.clientWidth - 1; this._hover.containerDomNode.classList.add('right-aligned'); } else { this._x = targetLeft; diff --git a/src/vs/workbench/services/hover/browser/media/hover.css b/src/vs/workbench/services/hover/browser/media/hover.css index 47d8ab484c6..6514844fd10 100644 --- a/src/vs/workbench/services/hover/browser/media/hover.css +++ b/src/vs/workbench/services/hover/browser/media/hover.css @@ -18,6 +18,11 @@ color: #3794ff; } +.monaco-workbench .workbench-hover.right-aligned { + /* The context view service wraps strangely when it's right up against the edge without this */ + left: 1px; +} + .monaco-workbench .workbench-hover.right-aligned .hover-row.status-bar .actions { flex-direction: row-reverse; } From 5fb7d2aa2f02e28d5be477c9309919e67bb40e0f Mon Sep 17 00:00:00 2001 From: Leila Pearson <873990+leilapearson@users.noreply.github.com> Date: Wed, 12 Aug 2020 16:33:14 -0700 Subject: [PATCH 183/736] Compare full filenames This change fixes issue #99955 When comparing file and folder names, after grouping by type if applicable, compare the full name all at once instead of separately comparing the name and the extension. This change makes filename comparisons work the same as they do in the Mac File Explorer and the same as a standard locale-based sort with the numeric option (a form of natural sort) enabled. Windows File Explorer has a different sort order due to treating the dot character differently. Many terminal programs and sites like github also differ, using a unicode-sort instead of a locale-based sort. --- src/vs/base/common/comparers.ts | 68 +++---- src/vs/base/test/browser/comparers.test.ts | 185 ++++++++---------- .../files/browser/views/explorerViewer.ts | 10 +- 3 files changed, 116 insertions(+), 147 deletions(-) diff --git a/src/vs/base/common/comparers.ts b/src/vs/base/common/comparers.ts index a25e2bb9c16..7b45ce5abb6 100644 --- a/src/vs/base/common/comparers.ts +++ b/src/vs/base/common/comparers.ts @@ -33,8 +33,7 @@ const intlFileNameCollatorNumericCaseInsenstive: IdleValue<{ collator: Intl.Coll return { collator: collator }; -}); - +});/** Compares filenames without distinguishing the name from the extension. Disambiguates by unicode comparison. */ export function compareFileNames(one: string | null, other: string | null, caseSensitive = false): number { const a = one || ''; const b = other || ''; @@ -49,36 +48,16 @@ export function compareFileNames(one: string | null, other: string | null, caseS return result; } -/** Compares filenames by name then extension, sorting numbers numerically instead of alphabetically. */ -export function compareFileNamesNumeric(one: string | null, other: string | null): number { - const [oneName, oneExtension] = extractNameAndExtension(one, true); - const [otherName, otherExtension] = extractNameAndExtension(other, true); +/** Compares filenames without distinguishing the name from the extension. Disambiguates by length, not unicode comparison. */ +export function compareFileNamesDefault(one: string | null, other: string | null): number { const collatorNumeric = intlFileNameCollatorNumeric.value.collator; - const collatorNumericCaseInsensitive = intlFileNameCollatorNumericCaseInsenstive.value.collator; - let result; + one = one || ''; + other = other || ''; - // Check for name differences, comparing numbers numerically instead of alphabetically. - result = compareAndDisambiguateByLength(collatorNumeric, oneName, otherName); - if (result !== 0) { - return result; - } - - // Check for case insensitive extension differences, comparing numbers numerically instead of alphabetically. - result = compareAndDisambiguateByLength(collatorNumericCaseInsensitive, oneExtension, otherExtension); - if (result !== 0) { - return result; - } - - // Disambiguate the extension case if needed. - if (oneExtension !== otherExtension) { - return collatorNumeric.compare(oneExtension, otherExtension); - } - - return 0; + // Compare the entire filename - both name and extension - and disambiguate by length if needed + return compareAndDisambiguateByLength(collatorNumeric, one, other); } -const FileNameMatch = /^(.*?)(\.([^.]*))?$/; - export function noIntlCompareFileNames(one: string | null, other: string | null, caseSensitive = false): number { if (!caseSensitive) { one = one && one.toLowerCase(); @@ -123,10 +102,12 @@ export function compareFileExtensions(one: string | null, other: string | null): return result; } -/** Compares filenames by extenson, then by name. Sorts numbers numerically, not alphabetically. */ -export function compareFileExtensionsNumeric(one: string | null, other: string | null): number { - const [oneName, oneExtension] = extractNameAndExtension(one, true); - const [otherName, otherExtension] = extractNameAndExtension(other, true); +/** Compares filenames by extenson, then by full filename */ +export function compareFileExtensionsDefault(one: string | null, other: string | null): number { + one = one || ''; + other = other || ''; + const oneExtension = extractExtension(one); + const otherExtension = extractExtension(other); const collatorNumeric = intlFileNameCollatorNumeric.value.collator; const collatorNumericCaseInsensitive = intlFileNameCollatorNumericCaseInsenstive.value.collator; let result; @@ -137,20 +118,12 @@ export function compareFileExtensionsNumeric(one: string | null, other: string | return result; } - // Compare names. - result = compareAndDisambiguateByLength(collatorNumeric, oneName, otherName); - if (result !== 0) { - return result; - } - - // Disambiguate extension case if needed. - if (oneExtension !== otherExtension) { - return collatorNumeric.compare(oneExtension, otherExtension); - } - - return 0; + // Compare full filenames + return compareAndDisambiguateByLength(collatorNumeric, one, other); } +const FileNameMatch = /^(.*?)(\.([^.]*))?$/; + /** Extracts the name and extension from a full filename, with optional special handling for dotfiles */ function extractNameAndExtension(str?: string | null, dotfilesAsNames = false): [string, string] { const match = str ? FileNameMatch.exec(str) as Array : ([] as Array); @@ -166,6 +139,13 @@ function extractNameAndExtension(str?: string | null, dotfilesAsNames = false): return result; } +/** Extracts the extension from a full filename. Treats dotfiles as names, not extensions. */ +function extractExtension(str?: string | null): string { + const match = str ? FileNameMatch.exec(str) as Array : ([] as Array); + + return (match && match[1] && match[1].charAt(0) !== '.' && match[3]) || ''; +} + function compareAndDisambiguateByLength(collator: Intl.Collator, one: string, other: string) { // Check for differences let result = collator.compare(one, other); diff --git a/src/vs/base/test/browser/comparers.test.ts b/src/vs/base/test/browser/comparers.test.ts index 90dff8c2835..4c40ea2a065 100644 --- a/src/vs/base/test/browser/comparers.test.ts +++ b/src/vs/base/test/browser/comparers.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { compareFileNames, compareFileExtensions, compareFileNamesNumeric, compareFileExtensionsNumeric } from 'vs/base/common/comparers'; +import { compareFileNames, compareFileExtensions, compareFileNamesDefault, compareFileExtensionsDefault } from 'vs/base/common/comparers'; import * as assert from 'assert'; const compareLocale = (a: string, b: string) => a.localeCompare(b); @@ -15,7 +15,7 @@ suite('Comparers', () => { test('compareFileNames', () => { // - // Comparisons with the same results as compareFileNamesNumeric + // Comparisons with the same results as compareFileNamesDefault // // name-only comparisons @@ -28,6 +28,7 @@ suite('Comparers', () => { // name plus extension comparisons assert(compareFileNames('bbb.aaa', 'aaa.bbb') > 0, 'files with extensions are compared first by filename'); + assert(compareFileNames('aggregate.go', 'aggregate_repo.go') > 0, 'compares the whole name all at once by locale'); // dotfile comparisons assert(compareFileNames('.abc', '.abc') === 0, 'equal dotfile names should be equal'); @@ -52,7 +53,7 @@ suite('Comparers', () => { assert(compareFileNames('abc1.10.txt', 'abc1.2.txt') > 0, 'numbers with dots between them are treated as two separate numbers, not one decimal number'); // - // Comparisons with different results than compareFileNamesNumeric + // Comparisons with different results than compareFileNamesDefault // // name-only comparisons @@ -61,9 +62,6 @@ suite('Comparers', () => { assert.notDeepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileNames), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases do not sort in locale order'); assert.notDeepEqual(['email', 'Email', 'émail', 'Émail'].sort(compareFileNames), ['email', 'Email', 'émail', 'Émail'].sort(compareLocale), 'the same base characters with different case or accents do not sort in locale order'); - // name plus extension comparisons - assert(compareFileNames('aggregate.go', 'aggregate_repo.go') > 0, 'compares the whole name all at once by locale'); - // numeric comparisons assert(compareFileNames('abc02.txt', 'abc002.txt') > 0, 'filenames with equivalent numbers and leading zeros sort in unicode order'); assert(compareFileNames('abc.txt1', 'abc.txt01') > 0, 'same name plus extensions with equal numbers sort in unicode order'); @@ -75,7 +73,7 @@ suite('Comparers', () => { test('compareFileExtensions', () => { // - // Comparisons with the same results as compareFileExtensionsNumeric + // Comparisons with the same results as compareFileExtensionsDefault // // name-only comparisons @@ -118,12 +116,8 @@ suite('Comparers', () => { assert(compareFileExtensions('a.ext1', 'b.ext1') < 0, 'if equal extensions with numbers, filenames should be compared'); assert(compareFileExtensions('a10.txt', 'A2.txt') > 0, 'filenames with number and case differences compare numerically'); - // Same extension comparison that has the same result as compareFileExtensionsNumeric, but a different result than compareFileNames - // This is an edge case caused by compareFileNames comparing the whole name all at once instead of the name and then the extension. - assert(compareFileExtensions('aggregate.go', 'aggregate_repo.go') < 0, 'when extensions are equal, names sort in dictionary order'); - // - // Comparisons with different results from compareFileExtensionsNumeric + // Comparisons with different results from compareFileExtensionsDefault // // name-only comparisions @@ -135,6 +129,7 @@ suite('Comparers', () => { // name plus extension comparisons assert(compareFileExtensions('a.MD', 'a.md') !== compareLocale('MD', 'md'), 'case differences in extensions do not sort by locale'); assert(compareFileExtensions('a.md', 'A.md') !== compareLocale('a', 'A'), 'case differences in names do not sort by locale'); + assert(compareFileExtensions('aggregate.go', 'aggregate_repo.go') < 0, 'when extensions are equal, names sort in dictionary order'); // dotfile comparisons assert(compareFileExtensions('.env', '.aaa.env') < 0, 'a dotfile with an extension is treated as a name plus an extension - equal extensions'); @@ -152,145 +147,139 @@ suite('Comparers', () => { }); - test('compareFileNamesNumeric', () => { + test('compareFileNamesDefault', () => { // // Comparisons with the same results as compareFileNames // // name-only comparisons - assert(compareFileNamesNumeric(null, null) === 0, 'null should be equal'); - assert(compareFileNamesNumeric(null, 'abc') < 0, 'null should be come before real values'); - assert(compareFileNamesNumeric('', '') === 0, 'empty should be equal'); - assert(compareFileNamesNumeric('abc', 'abc') === 0, 'equal names should be equal'); - assert(compareFileNamesNumeric('z', 'A') > 0, 'z comes is after A regardless of case'); - assert(compareFileNamesNumeric('Z', 'a') > 0, 'Z comes after a regardless of case'); + assert(compareFileNamesDefault(null, null) === 0, 'null should be equal'); + assert(compareFileNamesDefault(null, 'abc') < 0, 'null should be come before real values'); + assert(compareFileNamesDefault('', '') === 0, 'empty should be equal'); + assert(compareFileNamesDefault('abc', 'abc') === 0, 'equal names should be equal'); + assert(compareFileNamesDefault('z', 'A') > 0, 'z comes is after A regardless of case'); + assert(compareFileNamesDefault('Z', 'a') > 0, 'Z comes after a regardless of case'); // name plus extension comparisons - assert(compareFileNamesNumeric('file.ext', 'file.ext') === 0, 'equal full names should be equal'); - assert(compareFileNamesNumeric('a.ext', 'b.ext') < 0, 'if equal extensions, filenames should be compared'); - assert(compareFileNamesNumeric('file.aaa', 'file.bbb') < 0, 'files with equal names should be compared by extensions'); - assert(compareFileNamesNumeric('bbb.aaa', 'aaa.bbb') > 0, 'files should be compared by names even if extensions compare differently'); + assert(compareFileNamesDefault('file.ext', 'file.ext') === 0, 'equal full names should be equal'); + assert(compareFileNamesDefault('a.ext', 'b.ext') < 0, 'if equal extensions, filenames should be compared'); + assert(compareFileNamesDefault('file.aaa', 'file.bbb') < 0, 'files with equal names should be compared by extensions'); + assert(compareFileNamesDefault('bbb.aaa', 'aaa.bbb') > 0, 'files should be compared by names even if extensions compare differently'); + assert(compareFileNamesDefault('aggregate.go', 'aggregate_repo.go') > 0, 'compares the whole filename in locale order'); // dotfile comparisons - assert(compareFileNamesNumeric('.abc', '.abc') === 0, 'equal dotfile names should be equal'); - assert(compareFileNamesNumeric('.env.', '.gitattributes') < 0, 'filenames starting with dots and with extensions should still sort properly'); - assert(compareFileNamesNumeric('.env', '.aaa.env') > 0, 'dotfiles sort alphabetically when they contain multiple dots'); - assert(compareFileNamesNumeric('.env', '.env.aaa') < 0, 'dotfiles with the same root sort shortest first'); - assert(compareFileNamesNumeric('.aaa_env', '.aaa.env') < 0, 'and underscore in a dotfile name will sort before a dot'); + assert(compareFileNamesDefault('.abc', '.abc') === 0, 'equal dotfile names should be equal'); + assert(compareFileNamesDefault('.env.', '.gitattributes') < 0, 'filenames starting with dots and with extensions should still sort properly'); + assert(compareFileNamesDefault('.env', '.aaa.env') > 0, 'dotfiles sort alphabetically when they contain multiple dots'); + assert(compareFileNamesDefault('.env', '.env.aaa') < 0, 'dotfiles with the same root sort shortest first'); + assert(compareFileNamesDefault('.aaa_env', '.aaa.env') < 0, 'and underscore in a dotfile name will sort before a dot'); // dotfile vs non-dotfile comparisons - assert(compareFileNamesNumeric(null, '.abc') < 0, 'null should come before dotfiles'); - assert(compareFileNamesNumeric('.env', 'aaa') < 0, 'dotfiles come before filenames without extensions'); - assert(compareFileNamesNumeric('.env', 'aaa.env') < 0, 'dotfiles come before filenames with extensions'); - assert(compareFileNamesNumeric('.md', 'A.MD') < 0, 'dotfiles sort before uppercase files'); - assert(compareFileNamesNumeric('.MD', 'a.md') < 0, 'dotfiles sort before lowercase files'); + assert(compareFileNamesDefault(null, '.abc') < 0, 'null should come before dotfiles'); + assert(compareFileNamesDefault('.env', 'aaa') < 0, 'dotfiles come before filenames without extensions'); + assert(compareFileNamesDefault('.env', 'aaa.env') < 0, 'dotfiles come before filenames with extensions'); + assert(compareFileNamesDefault('.md', 'A.MD') < 0, 'dotfiles sort before uppercase files'); + assert(compareFileNamesDefault('.MD', 'a.md') < 0, 'dotfiles sort before lowercase files'); // numeric comparisons - assert(compareFileNamesNumeric('1', '1') === 0, 'numerically equal full names should be equal'); - assert(compareFileNamesNumeric('abc1.txt', 'abc1.txt') === 0, 'equal filenames with numbers should be equal'); - assert(compareFileNamesNumeric('abc1.txt', 'abc2.txt') < 0, 'filenames with numbers should be in numerical order, not alphabetical order'); - assert(compareFileNamesNumeric('abc2.txt', 'abc10.txt') < 0, 'filenames with numbers should be in numerical order even when they are multiple digits long'); - assert(compareFileNamesNumeric('abc02.txt', 'abc010.txt') < 0, 'filenames with numbers that have leading zeros sort numerically'); - assert(compareFileNamesNumeric('abc1.10.txt', 'abc1.2.txt') > 0, 'numbers with dots between them are treated as two separate numbers, not one decimal number'); + assert(compareFileNamesDefault('1', '1') === 0, 'numerically equal full names should be equal'); + assert(compareFileNamesDefault('abc1.txt', 'abc1.txt') === 0, 'equal filenames with numbers should be equal'); + assert(compareFileNamesDefault('abc1.txt', 'abc2.txt') < 0, 'filenames with numbers should be in numerical order, not alphabetical order'); + assert(compareFileNamesDefault('abc2.txt', 'abc10.txt') < 0, 'filenames with numbers should be in numerical order even when they are multiple digits long'); + assert(compareFileNamesDefault('abc02.txt', 'abc010.txt') < 0, 'filenames with numbers that have leading zeros sort numerically'); + assert(compareFileNamesDefault('abc1.10.txt', 'abc1.2.txt') > 0, 'numbers with dots between them are treated as two separate numbers, not one decimal number'); // // Comparisons with different results than compareFileNames // // name-only comparisons - assert(compareFileNamesNumeric('a', 'A') === compareLocale('a', 'A'), 'the same letter sorts by locale'); - assert(compareFileNamesNumeric('â', 'Â') === compareLocale('â', 'Â'), 'the same accented letter sorts by locale'); - assert.deepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileNamesNumeric), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases sort in locale order'); - assert.deepEqual(['email', 'Email', 'émail', 'Émail'].sort(compareFileNamesNumeric), ['email', 'Email', 'émail', 'Émail'].sort(compareLocale), 'the same base characters with different case or accents sort in locale order'); - - // name plus extensions comparisons - assert(compareFileNamesNumeric('aggregate.go', 'aggregate_repo.go') < 0, 'compares the name first, then the extension'); + assert(compareFileNamesDefault('a', 'A') === compareLocale('a', 'A'), 'the same letter sorts by locale'); + assert(compareFileNamesDefault('â', 'Â') === compareLocale('â', 'Â'), 'the same accented letter sorts by locale'); + assert.deepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileNamesDefault), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases sort in locale order'); + assert.deepEqual(['email', 'Email', 'émail', 'Émail'].sort(compareFileNamesDefault), ['email', 'Email', 'émail', 'Émail'].sort(compareLocale), 'the same base characters with different case or accents sort in locale order'); // numeric comparisons - assert(compareFileNamesNumeric('abc02.txt', 'abc002.txt') < 0, 'filenames with equivalent numbers and leading zeros sort shortest number first'); - assert(compareFileNamesNumeric('abc.txt1', 'abc.txt01') < 0, 'same name plus extensions with equal numbers sort shortest number first'); - assert(compareFileNamesNumeric('art01', 'Art01') === compareLocaleNumeric('art01', 'Art01'), 'a numerically equivalent word of a different case compares numerically based on locale'); + assert(compareFileNamesDefault('abc02.txt', 'abc002.txt') < 0, 'filenames with equivalent numbers and leading zeros sort shortest number first'); + assert(compareFileNamesDefault('abc.txt1', 'abc.txt01') < 0, 'same name plus extensions with equal numbers sort shortest number first'); + assert(compareFileNamesDefault('art01', 'Art01') === compareLocaleNumeric('art01', 'Art01'), 'a numerically equivalent word of a different case compares numerically based on locale'); }); - test('compareFileExtensionsNumeric', () => { + test('compareFileExtensionsDefault', () => { // // Comparisons with the same result as compareFileExtensions // // name-only comparisons - assert(compareFileExtensionsNumeric(null, null) === 0, 'null should be equal'); - assert(compareFileExtensionsNumeric(null, 'abc') < 0, 'null should come before real files without extensions'); - assert(compareFileExtensionsNumeric('', '') === 0, 'empty should be equal'); - assert(compareFileExtensionsNumeric('abc', 'abc') === 0, 'equal names should be equal'); - assert(compareFileExtensionsNumeric('z', 'A') > 0, 'z comes after A'); - assert(compareFileExtensionsNumeric('Z', 'a') > 0, 'Z comes after a'); + assert(compareFileExtensionsDefault(null, null) === 0, 'null should be equal'); + assert(compareFileExtensionsDefault(null, 'abc') < 0, 'null should come before real files without extensions'); + assert(compareFileExtensionsDefault('', '') === 0, 'empty should be equal'); + assert(compareFileExtensionsDefault('abc', 'abc') === 0, 'equal names should be equal'); + assert(compareFileExtensionsDefault('z', 'A') > 0, 'z comes after A'); + assert(compareFileExtensionsDefault('Z', 'a') > 0, 'Z comes after a'); // name plus extension comparisons - assert(compareFileExtensionsNumeric('file.ext', 'file.ext') === 0, 'equal full filenames should be equal'); - assert(compareFileExtensionsNumeric('a.ext', 'b.ext') < 0, 'if equal extensions, filenames should be compared'); - assert(compareFileExtensionsNumeric('file.aaa', 'file.bbb') < 0, 'files with equal names should be compared by extensions'); - assert(compareFileExtensionsNumeric('bbb.aaa', 'aaa.bbb') < 0, 'files should be compared by extension first'); - assert(compareFileExtensionsNumeric('agg.go', 'aggrepo.go') < 0, 'shorter names sort before longer names'); - assert(compareFileExtensionsNumeric('agg.go', 'agg_repo.go') < 0, 'shorter names short before longer names even when the longer name contains an underscore'); - assert(compareFileExtensionsNumeric('a.MD', 'b.md') < 0, 'when extensions are the same except for case, the files sort by name'); + assert(compareFileExtensionsDefault('file.ext', 'file.ext') === 0, 'equal full filenames should be equal'); + assert(compareFileExtensionsDefault('a.ext', 'b.ext') < 0, 'if equal extensions, filenames should be compared'); + assert(compareFileExtensionsDefault('file.aaa', 'file.bbb') < 0, 'files with equal names should be compared by extensions'); + assert(compareFileExtensionsDefault('bbb.aaa', 'aaa.bbb') < 0, 'files should be compared by extension first'); + assert(compareFileExtensionsDefault('agg.go', 'aggrepo.go') < 0, 'shorter names sort before longer names'); + assert(compareFileExtensionsDefault('a.MD', 'b.md') < 0, 'when extensions are the same except for case, the files sort by name'); // dotfile comparisons - assert(compareFileExtensionsNumeric('.abc', '.abc') === 0, 'equal dotfiles should be equal'); - assert(compareFileExtensionsNumeric('.md', '.Gitattributes') > 0, 'dotfiles sort alphabetically regardless of case'); + assert(compareFileExtensionsDefault('.abc', '.abc') === 0, 'equal dotfiles should be equal'); + assert(compareFileExtensionsDefault('.md', '.Gitattributes') > 0, 'dotfiles sort alphabetically regardless of case'); // dotfile vs non-dotfile comparisons - assert(compareFileExtensionsNumeric(null, '.abc') < 0, 'null should come before dotfiles'); - assert(compareFileExtensionsNumeric('.env', 'aaa.env') < 0, 'dotfiles come before filenames with extensions'); - assert(compareFileExtensionsNumeric('.MD', 'a.md') < 0, 'dotfiles sort before lowercase files'); + assert(compareFileExtensionsDefault(null, '.abc') < 0, 'null should come before dotfiles'); + assert(compareFileExtensionsDefault('.env', 'aaa.env') < 0, 'dotfiles come before filenames with extensions'); + assert(compareFileExtensionsDefault('.MD', 'a.md') < 0, 'dotfiles sort before lowercase files'); // numeric comparisons - assert(compareFileExtensionsNumeric('1', '1') === 0, 'numerically equal full names should be equal'); - assert(compareFileExtensionsNumeric('abc1.txt', 'abc1.txt') === 0, 'equal filenames with numbers should be equal'); - assert(compareFileExtensionsNumeric('abc1.txt', 'abc2.txt') < 0, 'filenames with numbers should be in numerical order, not alphabetical order'); - assert(compareFileExtensionsNumeric('abc2.txt', 'abc10.txt') < 0, 'filenames with numbers should be in numerical order'); - assert(compareFileExtensionsNumeric('abc02.txt', 'abc010.txt') < 0, 'filenames with numbers that have leading zeros sort numerically'); - assert(compareFileExtensionsNumeric('abc1.10.txt', 'abc1.2.txt') > 0, 'numbers with dots between them are treated as two separate numbers, not one decimal number'); - assert(compareFileExtensionsNumeric('abc2.txt2', 'abc1.txt10') < 0, 'extensions with numbers should be in numerical order, not alphabetical order'); - assert(compareFileExtensionsNumeric('txt.abc1', 'txt.abc1') === 0, 'equal extensions with numbers should be equal'); - assert(compareFileExtensionsNumeric('txt.abc1', 'txt.abc2') < 0, 'extensions with numbers should be in numerical order, not alphabetical order'); - assert(compareFileExtensionsNumeric('txt.abc2', 'txt.abc10') < 0, 'extensions with numbers should be in numerical order even when they are multiple digits long'); - assert(compareFileExtensionsNumeric('a.ext1', 'b.ext1') < 0, 'if equal extensions with numbers, filenames should be compared'); - assert(compareFileExtensionsNumeric('a10.txt', 'A2.txt') > 0, 'filenames with number and case differences compare numerically'); - - // Same extension comparison that has the same result as compareFileExtensions, but a different result than compareFileNames - // This is an edge case caused by compareFileNames comparing the whole name all at once instead of the name and then the extension. - assert(compareFileExtensionsNumeric('aggregate.go', 'aggregate_repo.go') < 0, 'when extensions are equal, names sort in dictionary order'); + assert(compareFileExtensionsDefault('1', '1') === 0, 'numerically equal full names should be equal'); + assert(compareFileExtensionsDefault('abc1.txt', 'abc1.txt') === 0, 'equal filenames with numbers should be equal'); + assert(compareFileExtensionsDefault('abc1.txt', 'abc2.txt') < 0, 'filenames with numbers should be in numerical order, not alphabetical order'); + assert(compareFileExtensionsDefault('abc2.txt', 'abc10.txt') < 0, 'filenames with numbers should be in numerical order'); + assert(compareFileExtensionsDefault('abc02.txt', 'abc010.txt') < 0, 'filenames with numbers that have leading zeros sort numerically'); + assert(compareFileExtensionsDefault('abc1.10.txt', 'abc1.2.txt') > 0, 'numbers with dots between them are treated as two separate numbers, not one decimal number'); + assert(compareFileExtensionsDefault('abc2.txt2', 'abc1.txt10') < 0, 'extensions with numbers should be in numerical order, not alphabetical order'); + assert(compareFileExtensionsDefault('txt.abc1', 'txt.abc1') === 0, 'equal extensions with numbers should be equal'); + assert(compareFileExtensionsDefault('txt.abc1', 'txt.abc2') < 0, 'extensions with numbers should be in numerical order, not alphabetical order'); + assert(compareFileExtensionsDefault('txt.abc2', 'txt.abc10') < 0, 'extensions with numbers should be in numerical order even when they are multiple digits long'); + assert(compareFileExtensionsDefault('a.ext1', 'b.ext1') < 0, 'if equal extensions with numbers, filenames should be compared'); + assert(compareFileExtensionsDefault('a10.txt', 'A2.txt') > 0, 'filenames with number and case differences compare numerically'); // // Comparisons with different results than compareFileExtensions // // name-only comparisons - assert(compareFileExtensionsNumeric('a', 'A') === compareLocale('a', 'A'), 'the same letter of different case sorts by locale'); - assert(compareFileExtensionsNumeric('â', 'Â') === compareLocale('â', 'Â'), 'the same accented letter of different case sorts by locale'); - assert.deepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileExtensionsNumeric), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases sort in locale order'); - assert.deepEqual(['email', 'Email', 'émail', 'Émail'].sort(compareFileExtensionsNumeric), ['email', 'Email', 'émail', 'Émail'].sort((a, b) => a.localeCompare(b)), 'the same base characters with different case or accents sort in locale order'); + assert(compareFileExtensionsDefault('a', 'A') === compareLocale('a', 'A'), 'the same letter of different case sorts by locale'); + assert(compareFileExtensionsDefault('â', 'Â') === compareLocale('â', 'Â'), 'the same accented letter of different case sorts by locale'); + assert.deepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileExtensionsDefault), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases sort in locale order'); + assert.deepEqual(['email', 'Email', 'émail', 'Émail'].sort(compareFileExtensionsDefault), ['email', 'Email', 'émail', 'Émail'].sort((a, b) => a.localeCompare(b)), 'the same base characters with different case or accents sort in locale order'); // name plus extension comparisons - assert(compareFileExtensionsNumeric('a.MD', 'a.md') === compareLocale('MD', 'md'), 'case differences in extensions sort by locale'); - assert(compareFileExtensionsNumeric('a.md', 'A.md') === compareLocale('a', 'A'), 'case differences in names sort by locale'); + assert(compareFileExtensionsDefault('a.MD', 'a.md') === compareLocale('MD', 'md'), 'case differences in extensions sort by locale'); + assert(compareFileExtensionsDefault('a.md', 'A.md') === compareLocale('a', 'A'), 'case differences in names sort by locale'); + assert(compareFileExtensionsDefault('aggregate.go', 'aggregate_repo.go') > 0, 'names with the same extension sort in full filename locale order'); // dotfile comparisons - assert(compareFileExtensionsNumeric('.env', '.aaa.env') > 0, 'dotfiles sort alphabetically when they contain multiple dots'); - assert(compareFileExtensionsNumeric('.env', '.env.aaa') < 0, 'dotfiles with the same root sort shortest first'); + assert(compareFileExtensionsDefault('.env', '.aaa.env') > 0, 'dotfiles sort alphabetically when they contain multiple dots'); + assert(compareFileExtensionsDefault('.env', '.env.aaa') < 0, 'dotfiles with the same root sort shortest first'); // dotfile vs non-dotfile comparisons - assert(compareFileExtensionsNumeric('.env', 'aaa') < 0, 'dotfiles come before filenames without extensions'); - assert(compareFileExtensionsNumeric('.md', 'A.MD') < 0, 'dotfiles sort before uppercase files'); + assert(compareFileExtensionsDefault('.env', 'aaa') < 0, 'dotfiles come before filenames without extensions'); + assert(compareFileExtensionsDefault('.md', 'A.MD') < 0, 'dotfiles sort before uppercase files'); // numeric comparisons - assert(compareFileExtensionsNumeric('abc.txt01', 'abc.txt1') > 0, 'extensions with equal numbers should be in shortest-first order'); - assert(compareFileExtensionsNumeric('art01', 'Art01') === compareLocaleNumeric('art01', 'Art01'), 'a numerically equivalent word of a different case compares numerically based on locale'); - assert(compareFileExtensionsNumeric('abc02.txt', 'abc002.txt') < 0, 'filenames with equivalent numbers and leading zeros sort shortest string first'); - assert(compareFileExtensionsNumeric('txt.abc01', 'txt.abc1') > 0, 'extensions with equivalent numbers sort shortest extension first'); + assert(compareFileExtensionsDefault('abc.txt01', 'abc.txt1') > 0, 'extensions with equal numbers should be in shortest-first order'); + assert(compareFileExtensionsDefault('art01', 'Art01') === compareLocaleNumeric('art01', 'Art01'), 'a numerically equivalent word of a different case compares numerically based on locale'); + assert(compareFileExtensionsDefault('abc02.txt', 'abc002.txt') < 0, 'filenames with equivalent numbers and leading zeros sort shortest string first'); + assert(compareFileExtensionsDefault('txt.abc01', 'txt.abc1') > 0, 'extensions with equivalent numbers sort shortest extension first'); }); }); diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 08a100254f8..2d940ea198a 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -29,7 +29,7 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { equals, deepClone } from 'vs/base/common/objects'; import * as path from 'vs/base/common/path'; import { ExplorerItem, NewExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel'; -import { compareFileExtensionsNumeric, compareFileNamesNumeric } from 'vs/base/common/comparers'; +import { compareFileNamesDefault, compareFileExtensionsDefault } from 'vs/base/common/comparers'; import { fillResourceDataTransfers, CodeDataTransfers, extractResources, containsDragType } from 'vs/workbench/browser/dnd'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IDragAndDropData, DataTransfers } from 'vs/base/browser/dnd'; @@ -665,7 +665,7 @@ export class FileSorter implements ITreeSorter { } if (statA.isDirectory && statB.isDirectory) { - return compareFileNamesNumeric(statA.name, statB.name); + return compareFileNamesDefault(statA.name, statB.name); } break; @@ -699,17 +699,17 @@ export class FileSorter implements ITreeSorter { // Sort Files switch (sortOrder) { case 'type': - return compareFileExtensionsNumeric(statA.name, statB.name); + return compareFileExtensionsDefault(statA.name, statB.name); case 'modified': if (statA.mtime !== statB.mtime) { return (statA.mtime && statB.mtime && statA.mtime < statB.mtime) ? 1 : -1; } - return compareFileNamesNumeric(statA.name, statB.name); + return compareFileNamesDefault(statA.name, statB.name); default: /* 'default', 'mixed', 'filesFirst' */ - return compareFileNamesNumeric(statA.name, statB.name); + return compareFileNamesDefault(statA.name, statB.name); } } } From 9a2696d39168acf9962bb5dacd5828dab749004f Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 12 Aug 2020 16:33:55 -0700 Subject: [PATCH 184/736] Don't fire onDidChangeTerminalDimensions when cols/rows is 0 Fixes #83778 --- .../vscode-api-tests/src/singlefolder-tests/terminal.test.ts | 4 ---- src/vs/workbench/api/common/extHostTerminalService.ts | 3 +++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts index 8b2f8c057e0..1e3ea7e9dd1 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -460,10 +460,6 @@ import { doesNotThrow, equal, ok, deepEqual, throws } from 'assert'; } term.show(); disposables.push(window.onDidChangeTerminalDimensions(e => { - if (e.dimensions.columns === 0 || e.dimensions.rows === 0) { - // HACK: Ignore the event if dimension(s) are zero (#83778) - return; - } // The default pty dimensions have a chance to appear here since override // dimensions happens after the terminal is created. If so just ignore and // wait for the right dimensions diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index d49cb264c21..ad449efd0da 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -175,6 +175,9 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi // Nothing changed return false; } + if (cols === 0 || rows === 0) { + return false; + } this._cols = cols; this._rows = rows; return true; From 49924e7ba05843a0962f68b5aa36208014a3fe0a Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 12 Aug 2020 16:35:56 -0700 Subject: [PATCH 185/736] debug: improve self-host debug configurations https://github.com/microsoft/vscode/issues/100368 --- .vscode/launch.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 577b733df80..1d966c8b508 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -226,7 +226,9 @@ "--no-cached-data", ], "webRoot": "${workspaceFolder}", - // Settings for js-debug: + "cascadeTerminateToConfigurations": [ + "Attach to Extension Host" + ], "userDataDir": false, "pauseForSourceMap": false, "outFiles": [ @@ -436,6 +438,7 @@ "Attach to Extension Host", "Attach to Shared Process", ], + "preLaunchTask": "Ensure Prelaunch Dependencies", "presentation": { "group": "0_vscode", "order": 1 From c12abc9bb404f03b19624e5985b3b3ab46b1ddea Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 12 Aug 2020 17:01:08 -0700 Subject: [PATCH 186/736] fixes #104448 --- src/vs/workbench/common/views.ts | 1 + .../services/views/browser/viewDescriptorService.ts | 2 +- .../workbench/services/views/common/viewContainerModel.ts | 8 ++++---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index b0712b4e90b..3d327ef6f64 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -244,6 +244,7 @@ export interface IAddedViewDescriptorRef extends IViewDescriptorRef { export interface IAddedViewDescriptorState { viewDescriptor: IViewDescriptor, collapsed?: boolean; + visible?: boolean; } export interface IViewContainerModel { diff --git a/src/vs/workbench/services/views/browser/viewDescriptorService.ts b/src/vs/workbench/services/views/browser/viewDescriptorService.ts index ea89babdc73..f8c3d2107c2 100644 --- a/src/vs/workbench/services/views/browser/viewDescriptorService.ts +++ b/src/vs/workbench/services/views/browser/viewDescriptorService.ts @@ -692,7 +692,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor }); }); - this.getViewContainerModel(container).add(views.map(view => { return { viewDescriptor: view, collapsed: expandViews ? false : undefined }; })); + this.getViewContainerModel(container).add(views.map(view => { return { viewDescriptor: view, collapsed: expandViews ? false : undefined, visible: expandViews }; })); } private removeViews(container: ViewContainer, views: IViewDescriptor[]): void { diff --git a/src/vs/workbench/services/views/common/viewContainerModel.ts b/src/vs/workbench/services/views/common/viewContainerModel.ts index 60c69da9c41..a8e7d7ccee7 100644 --- a/src/vs/workbench/services/views/common/viewContainerModel.ts +++ b/src/vs/workbench/services/views/common/viewContainerModel.ts @@ -478,16 +478,16 @@ export class ViewContainerModel extends Disposable implements IViewContainerMode if (state) { // set defaults if not set if (viewDescriptor.workspace) { - state.visibleWorkspace = isUndefinedOrNull(state.visibleWorkspace) ? !viewDescriptor.hideByDefault : state.visibleWorkspace; + state.visibleWorkspace = isUndefinedOrNull(addedViewDescriptorState.visible) ? (isUndefinedOrNull(state.visibleWorkspace) ? !viewDescriptor.hideByDefault : state.visibleWorkspace) : addedViewDescriptorState.visible; } else { - state.visibleGlobal = isUndefinedOrNull(state.visibleGlobal) ? !viewDescriptor.hideByDefault : state.visibleGlobal; + state.visibleGlobal = isUndefinedOrNull(addedViewDescriptorState.visible) ? (isUndefinedOrNull(state.visibleGlobal) ? !viewDescriptor.hideByDefault : state.visibleGlobal) : addedViewDescriptorState.visible; } state.collapsed = isUndefinedOrNull(addedViewDescriptorState.collapsed) ? (isUndefinedOrNull(state.collapsed) ? !!viewDescriptor.collapsed : state.collapsed) : addedViewDescriptorState.collapsed; } else { state = { active: false, - visibleGlobal: !viewDescriptor.hideByDefault, - visibleWorkspace: !viewDescriptor.hideByDefault, + visibleGlobal: isUndefinedOrNull(addedViewDescriptorState.visible) ? !viewDescriptor.hideByDefault : addedViewDescriptorState.visible, + visibleWorkspace: isUndefinedOrNull(addedViewDescriptorState.visible) ? !viewDescriptor.hideByDefault : addedViewDescriptorState.visible, collapsed: isUndefinedOrNull(addedViewDescriptorState.collapsed) ? !!viewDescriptor.collapsed : addedViewDescriptorState.collapsed, }; } From f7f05dee53fb33fe023db2e06e30a89d3094488f Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 12 Aug 2020 17:02:26 -0700 Subject: [PATCH 187/736] Don't require creationOptions to be the same object Fixes #102950 --- .../src/singlefolder-tests/terminal.test.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts index 1e3ea7e9dd1..8e2afa3ba13 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { window, Pseudoterminal, EventEmitter, TerminalDimensions, workspace, ConfigurationTarget, Disposable, UIKind, env, EnvironmentVariableMutatorType, EnvironmentVariableMutator, extensions, ExtensionContext } from 'vscode'; +import { window, Pseudoterminal, EventEmitter, TerminalDimensions, workspace, ConfigurationTarget, Disposable, UIKind, env, EnvironmentVariableMutatorType, EnvironmentVariableMutator, extensions, ExtensionContext, TerminalOptions, ExtensionTerminalOptions } from 'vscode'; import { doesNotThrow, equal, ok, deepEqual, throws } from 'assert'; // Disable terminal tests: @@ -168,8 +168,10 @@ import { doesNotThrow, equal, ok, deepEqual, throws } from 'assert'; const terminal = window.createTerminal(options); try { equal(terminal.name, 'foo'); - deepEqual(terminal.creationOptions, options); - throws(() => (terminal.creationOptions).name = 'bad', 'creationOptions should be readonly at runtime'); + const terminalOptions = terminal.creationOptions as TerminalOptions; + equal(terminalOptions.name, 'foo'); + equal(terminalOptions.hideFromUser, true); + throws(() => terminalOptions.name = 'bad', 'creationOptions should be readonly at runtime'); } catch (e) { done(e); return; @@ -605,8 +607,10 @@ import { doesNotThrow, equal, ok, deepEqual, throws } from 'assert'; const terminal = window.createTerminal(options); try { equal(terminal.name, 'foo'); - deepEqual(terminal.creationOptions, options); - throws(() => (terminal.creationOptions).name = 'bad', 'creationOptions should be readonly at runtime'); + const terminalOptions = terminal.creationOptions as ExtensionTerminalOptions; + equal(terminalOptions.name, 'foo'); + equal(terminalOptions.pty, pty); + throws(() => terminalOptions.name = 'bad', 'creationOptions should be readonly at runtime'); } catch (e) { done(e); } From 34dee12ff383689d9d5e62f6b263ed644e4df44d Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 12 Aug 2020 22:11:34 -0700 Subject: [PATCH 188/736] MOVE_EDITOR context not used when dragging between groups Fix #104535 --- src/vs/workbench/browser/parts/editor/editorDropTarget.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index bc9f5db8216..d7ae2ba76a1 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -12,7 +12,7 @@ import { IThemeService, Themable } from 'vs/platform/theme/common/themeService'; import { activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { IEditorIdentifier, EditorInput, EditorOptions } from 'vs/workbench/common/editor'; import { isMacintosh, isWeb } from 'vs/base/common/platform'; -import { GroupDirection, MergeGroupMode } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { GroupDirection, MergeGroupMode, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService'; import { toDisposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -277,13 +277,13 @@ class DropOverlay extends Themable { pinned: true, // always pin dropped editor sticky: sourceGroup.isSticky(draggedEditor.editor) // preserve sticky state })); - targetGroup.openEditor(draggedEditor.editor, options); + const copyEditor = this.isCopyOperation(event, draggedEditor); + targetGroup.openEditor(draggedEditor.editor, options, copyEditor ? OpenEditorContext.COPY_EDITOR : OpenEditorContext.MOVE_EDITOR); // Ensure target has focus targetGroup.focus(); // Close in source group unless we copy - const copyEditor = this.isCopyOperation(event, draggedEditor); if (!copyEditor) { sourceGroup.closeEditor(draggedEditor.editor); } From f0f223a1f1ad0004ead81de53d0e2d35dfae50e8 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Aug 2020 09:45:04 +0200 Subject: [PATCH 189/736] :lipstick: binary file editor --- .../files/browser/editors/binaryFileEditor.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts index 1daf1517449..7b93fdb20c7 100644 --- a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts @@ -49,13 +49,13 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor { } private async openInternal(input: EditorInput, options: EditorOptions | undefined): Promise { - if (input instanceof FileEditorInput) { + if (input instanceof FileEditorInput && this.group) { + + // Enforce to open the input as text to enable our text based viewer input.setForceOpenAsText(); - if (this.group !== undefined) { - await openEditorWith(input, undefined, options, this.group, this.editorService, this.configurationService, this.quickInputService); - } else { - await this.editorService.openEditor(input, options, this.group); - } + + // If more editors are installed that can handle this input, show a picker + await openEditorWith(input, undefined, options, this.group, this.editorService, this.configurationService, this.quickInputService); } } From 6e57a9a4bb01135fe035f794ffbbd351f807c7a9 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Aug 2020 09:59:09 +0200 Subject: [PATCH 190/736] nls - remove legacy local.json migration --- src/main.js | 39 ++------------------------------------- 1 file changed, 2 insertions(+), 37 deletions(-) diff --git a/src/main.js b/src/main.js index 9a49d06217a..9d68c984c66 100644 --- a/src/main.js +++ b/src/main.js @@ -313,17 +313,6 @@ function createDefaultArgvConfigSync(argvConfigPath) { fs.mkdirSync(argvConfigPathDirname); } - // Migrate over legacy locale - const localeConfigPath = path.join(userDataPath, 'User', 'locale.json'); - const legacyLocale = getLegacyUserDefinedLocaleSync(localeConfigPath); - if (legacyLocale) { - try { - fs.unlinkSync(localeConfigPath); - } catch (error) { - //ignore - } - } - // Default argv content const defaultArgvConfigContent = [ '// This configuration file allows you to pass permanent command line arguments to VS Code.', @@ -340,19 +329,10 @@ function createDefaultArgvConfigSync(argvConfigPath) { '', ' // Enabled by default by VS Code to resolve color issues in the renderer', ' // See https://github.com/Microsoft/vscode/issues/51791 for details', - ' "disable-color-correct-rendering": true' + ' "disable-color-correct-rendering": true', + '}' ]; - if (legacyLocale) { - defaultArgvConfigContent[defaultArgvConfigContent.length - 1] = `${defaultArgvConfigContent[defaultArgvConfigContent.length - 1]},`; // append trailing "," - - defaultArgvConfigContent.push(''); - defaultArgvConfigContent.push(' // Display language of VS Code'); - defaultArgvConfigContent.push(` "locale": "${legacyLocale}"`); - } - - defaultArgvConfigContent.push('}'); - // Create initial argv.json with default content fs.writeFileSync(argvConfigPath, defaultArgvConfigContent.join('\n')); } catch (error) { @@ -610,19 +590,4 @@ function getUserDefinedLocale(argvConfig) { return argvConfig.locale && typeof argvConfig.locale === 'string' ? argvConfig.locale.toLowerCase() : undefined; } -/** - * @param {string} localeConfigPath - * @returns {string | undefined} - */ -function getLegacyUserDefinedLocaleSync(localeConfigPath) { - try { - const content = stripComments(fs.readFileSync(localeConfigPath).toString()); - - const value = JSON.parse(content).locale; - return value && typeof value === 'string' ? value.toLowerCase() : undefined; - } catch (error) { - // ignore - } -} - //#endregion From aa6e57d93c9d71e096849f621304cda57e483571 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Aug 2020 10:11:10 +0200 Subject: [PATCH 191/736] notifications - enable ellipsis overflow for buttons again --- .../browser/parts/notifications/media/notificationsList.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css index 9d01ee4526b..c6be6928d49 100644 --- a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css +++ b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css @@ -115,6 +115,10 @@ text-overflow: ellipsis; } +.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container .monaco-text-button { + display: inline-block; /* to enable ellipsis in text overflow */ +} + /** Notification: Progress */ .monaco-workbench .notifications-list-container .progress-bit { From 186c5879ce53aeb18cc2ef3a1eba68caf9270650 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 13 Aug 2020 10:32:55 +0200 Subject: [PATCH 192/736] Revert "maybe maybe for https://github.com/microsoft/vscode/issues/104485" This reverts commit af3c1cfdef66271159d8ad54adaf5f067810065c. --- src/vs/editor/contrib/suggest/test/suggestModel.test.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/vs/editor/contrib/suggest/test/suggestModel.test.ts b/src/vs/editor/contrib/suggest/test/suggestModel.test.ts index 7373a0db710..360ce3a79d1 100644 --- a/src/vs/editor/contrib/suggest/test/suggestModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/suggestModel.test.ts @@ -35,15 +35,12 @@ import { MockKeybindingService } from 'vs/platform/keybinding/test/common/mockKe import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { mock } from 'vs/base/test/common/mock'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; function createMockEditor(model: TextModel): ITestCodeEditor { let editor = createTestCodeEditor({ model: model, serviceCollection: new ServiceCollection( - [IConfigurationService, TestConfigurationService], [ITelemetryService, NullTelemetryService], [IStorageService, new InMemoryStorageService()], [IKeybindingService, new MockKeybindingService()], From 78ba6126c63c2768331420ca56e07df135b2d7b6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 13 Aug 2020 10:36:46 +0200 Subject: [PATCH 193/736] make sure to dispose worker service when test is done, should fix https://github.com/microsoft/vscode/issues/104485 --- src/vs/editor/contrib/suggest/test/wordDistance.test.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/editor/contrib/suggest/test/wordDistance.test.ts b/src/vs/editor/contrib/suggest/test/wordDistance.test.ts index 7f1acc79616..05ccef17d53 100644 --- a/src/vs/editor/contrib/suggest/test/wordDistance.test.ts +++ b/src/vs/editor/contrib/suggest/test/wordDistance.test.ts @@ -81,11 +81,16 @@ suite('suggest, word distance', function () { distance = await WordDistance.create(service, editor); + disposables.add(service); disposables.add(mode); disposables.add(model); disposables.add(editor); }); + teardown(function () { + disposables.clear(); + }); + function createSuggestItem(label: string, overwriteBefore: number, position: IPosition): CompletionItem { const suggestion: modes.CompletionItem = { label, From ad415a08c6262e619255a8eae34157dd8d0b0968 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Aug 2020 10:39:41 +0200 Subject: [PATCH 194/736] web - remove shell_exec from php sample --- extensions/vscode-web-playground/src/memfs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/vscode-web-playground/src/memfs.ts b/extensions/vscode-web-playground/src/memfs.ts index 969b2c4b6fa..76c6f3f0e2e 100644 --- a/extensions/vscode-web-playground/src/memfs.ts +++ b/extensions/vscode-web-playground/src/memfs.ts @@ -107,7 +107,7 @@ export class MemFS implements FileSystemProvider, FileSearchProvider, TextSearch this.writeFile(Uri.parse(`memfs:/sample-folder/file.py`), textEncoder.encode('import base64, sys; base64.decode(open(sys.argv[1], "rb"), open(sys.argv[2], "wb"))'), { create: true, overwrite: true }); this.writeFile(Uri.parse(`memfs:/sample-folder/file.yaml`), textEncoder.encode('- just: write something'), { create: true, overwrite: true }); this.writeFile(Uri.parse(`memfs:/sample-folder/file.jpg`), getImageFile(), { create: true, overwrite: true }); - this.writeFile(Uri.parse(`memfs:/sample-folder/file.php`), textEncoder.encode('&1\'); ?>'), { create: true, overwrite: true }); + this.writeFile(Uri.parse(`memfs:/sample-folder/file.php`), textEncoder.encode(''), { create: true, overwrite: true }); // some more files & folders this.createDirectory(Uri.parse(`memfs:/sample-folder/folder/`)); From 1d08d20301e8765ba1c870b637609df19053fc66 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 13 Aug 2020 11:27:16 +0200 Subject: [PATCH 195/736] Position tree markdown hovers with mouse x --- src/vs/workbench/contrib/views/browser/treeView.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/views/browser/treeView.ts b/src/vs/workbench/contrib/views/browser/treeView.ts index b13c36fadfb..1d46a7bf26a 100644 --- a/src/vs/workbench/contrib/views/browser/treeView.ts +++ b/src/vs/workbench/contrib/views/browser/treeView.ts @@ -38,7 +38,7 @@ import { FuzzyScore, createMatches } from 'vs/base/common/filters'; import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults'; import { isFalsyOrWhitespace } from 'vs/base/common/strings'; import { SIDE_BAR_BACKGROUND, PANEL_BACKGROUND } from 'vs/workbench/common/theme'; -import { IHoverService, IHoverOptions } from 'vs/workbench/services/hover/browser/hover'; +import { IHoverService, IHoverOptions, IHoverTarget } from 'vs/workbench/services/hover/browser/hover'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; class Root implements ITreeItem { @@ -829,8 +829,13 @@ class TreeRenderer extends Disposable implements ITreeRenderer { } + }; + hoverOptions = { text: isString(tooltip) ? { value: tooltip } : tooltip, target }; } + (hoverOptions.target).x = e.x; hoverService.showHover(hoverOptions); } this.removeEventListener(DOM.EventType.MOUSE_LEAVE, mouseLeave); From aabb4e3aa3313db938af851e372162a88c93ba21 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 13 Aug 2020 11:42:49 +0200 Subject: [PATCH 196/736] add TextDocument#notebook api proposal, https://github.com/microsoft/vscode/issues/100890 --- src/vs/vscode.proposed.d.ts | 8 +++ .../api/common/extHostDocumentData.ts | 51 +++++++++---------- .../api/common/extHostDocumentsAndEditors.ts | 20 ++++++-- .../workbench/api/common/extHostNotebook.ts | 19 ++++--- .../browser/api/extHostDocumentData.test.ts | 14 ++--- .../test/browser/api/extHostNotebook.test.ts | 8 +++ .../browser/api/extHostTextEditor.test.ts | 2 +- 7 files changed, 75 insertions(+), 47 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index c261d8647b8..50fcaed068f 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -2024,4 +2024,12 @@ declare module 'vscode' { } //#endregion + + + //#region https://github.com/microsoft/vscode/issues/102091 + + export interface TextDocument { + notebook: NotebookDocument | undefined; + } + //#endregion } diff --git a/src/vs/workbench/api/common/extHostDocumentData.ts b/src/vs/workbench/api/common/extHostDocumentData.ts index 756f1a03f84..0cd5c8378fc 100644 --- a/src/vs/workbench/api/common/extHostDocumentData.ts +++ b/src/vs/workbench/api/common/extHostDocumentData.ts @@ -29,19 +29,17 @@ export function getWordDefinitionFor(modeId: string): RegExp | undefined { export class ExtHostDocumentData extends MirrorTextModel { - private _proxy: MainThreadDocumentsShape; - private _languageId: string; - private _isDirty: boolean; private _document?: vscode.TextDocument; private _isDisposed: boolean = false; - constructor(proxy: MainThreadDocumentsShape, uri: URI, lines: string[], eol: string, - languageId: string, versionId: number, isDirty: boolean + constructor( + private readonly _proxy: MainThreadDocumentsShape, + uri: URI, lines: string[], eol: string, versionId: number, + private _languageId: string, + private _isDirty: boolean, + private readonly _notebook?: vscode.NotebookDocument | undefined ) { super(uri, lines, eol, versionId); - this._proxy = proxy; - this._languageId = languageId; - this._isDirty = isDirty; } dispose(): void { @@ -59,25 +57,26 @@ export class ExtHostDocumentData extends MirrorTextModel { get document(): vscode.TextDocument { if (!this._document) { - const data = this; + const that = this; this._document = { - get uri() { return data._uri; }, - get fileName() { return data._uri.fsPath; }, - get isUntitled() { return data._uri.scheme === Schemas.untitled; }, - get languageId() { return data._languageId; }, - get version() { return data._versionId; }, - get isClosed() { return data._isDisposed; }, - get isDirty() { return data._isDirty; }, - save() { return data._save(); }, - getText(range?) { return range ? data._getTextInRange(range) : data.getText(); }, - get eol() { return data._eol === '\n' ? EndOfLine.LF : EndOfLine.CRLF; }, - get lineCount() { return data._lines.length; }, - lineAt(lineOrPos: number | vscode.Position) { return data._lineAt(lineOrPos); }, - offsetAt(pos) { return data._offsetAt(pos); }, - positionAt(offset) { return data._positionAt(offset); }, - validateRange(ran) { return data._validateRange(ran); }, - validatePosition(pos) { return data._validatePosition(pos); }, - getWordRangeAtPosition(pos, regexp?) { return data._getWordRangeAtPosition(pos, regexp); } + get uri() { return that._uri; }, + get fileName() { return that._uri.fsPath; }, + get isUntitled() { return that._uri.scheme === Schemas.untitled; }, + get languageId() { return that._languageId; }, + get version() { return that._versionId; }, + get isClosed() { return that._isDisposed; }, + get isDirty() { return that._isDirty; }, + get notebook() { return that._notebook; }, + save() { return that._save(); }, + getText(range?) { return range ? that._getTextInRange(range) : that.getText(); }, + get eol() { return that._eol === '\n' ? EndOfLine.LF : EndOfLine.CRLF; }, + get lineCount() { return that._lines.length; }, + lineAt(lineOrPos: number | vscode.Position) { return that._lineAt(lineOrPos); }, + offsetAt(pos) { return that._offsetAt(pos); }, + positionAt(offset) { return that._positionAt(offset); }, + validateRange(ran) { return that._validateRange(ran); }, + validatePosition(pos) { return that._validatePosition(pos); }, + getWordRangeAtPosition(pos, regexp?) { return that._getWordRangeAtPosition(pos, regexp); }, }; } return Object.freeze(this._document); diff --git a/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts b/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts index 1518dc43d00..e378b49a198 100644 --- a/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts +++ b/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts @@ -4,11 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'vs/base/common/assert'; +import * as vscode from 'vscode'; import { Emitter, Event } from 'vs/base/common/event'; import { dispose } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { ExtHostDocumentsAndEditorsShape, IDocumentsAndEditorsDelta, MainContext } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostDocumentsAndEditorsShape, IDocumentsAndEditorsDelta, IModelAddedData, MainContext } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { ExtHostTextEditor } from 'vs/workbench/api/common/extHostTextEditor'; @@ -29,6 +30,14 @@ class Reference { } } +export interface IExtHostModelAddedData extends IModelAddedData { + notebook?: vscode.NotebookDocument; +} + +export interface IExtHostDocumentsAndEditorsDelta extends IDocumentsAndEditorsDelta { + addedDocuments?: IExtHostModelAddedData[]; +} + export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsShape { readonly _serviceBrand: undefined; @@ -54,6 +63,10 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha ) { } $acceptDocumentsAndEditorsDelta(delta: IDocumentsAndEditorsDelta): void { + this.acceptDocumentsAndEditorsDelta(delta); + } + + acceptDocumentsAndEditorsDelta(delta: IExtHostDocumentsAndEditorsDelta): void { const removedDocuments: ExtHostDocumentData[] = []; const addedDocuments: ExtHostDocumentData[] = []; @@ -88,9 +101,10 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha resource, data.lines, data.EOL, - data.modeId, data.versionId, - data.isDirty + data.modeId, + data.isDirty, + data.notebook )); this._documents.set(resource, ref); addedDocuments.push(ref.value); diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 8c5266dc2d8..6452d5a3985 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -17,7 +17,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' import { CellKind, ExtHostNotebookShape, IMainContext, IModelAddedData, INotebookDocumentsAndEditorsDelta, INotebookEditorPropertiesChangeData, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice } from 'vs/workbench/api/common/extHost.protocol'; import { ILogService } from 'vs/platform/log/common/log'; import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; -import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; +import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; @@ -61,14 +61,15 @@ const addIdToOutput = (output: IRawOutput, id = UUID.generateUuid()): IProcessed export class ExtHostCell extends Disposable implements vscode.NotebookCell { - public static asModelAddData(cell: IMainCellDto): IModelAddedData { + public static asModelAddData(notebook: ExtHostNotebookDocument, cell: IMainCellDto): IExtHostModelAddedData { return { EOL: cell.eol, lines: cell.source, modeId: cell.language, uri: cell.uri, isDirty: false, - versionId: 1 + versionId: 1, + notebook }; } @@ -363,7 +364,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo } const contentChangeEvents: vscode.NotebookCellsChangeData[] = []; - const addedCellDocuments: IModelAddedData[] = []; + const addedCellDocuments: IExtHostModelAddedData[] = []; splices.reverse().forEach(splice => { const cellDtos = splice[2]; @@ -372,7 +373,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo const extCell = new ExtHostCell(this._proxy, this, this._documentsAndEditors, cell); if (!initialization) { - addedCellDocuments.push(ExtHostCell.asModelAddData(cell)); + addedCellDocuments.push(ExtHostCell.asModelAddData(this, cell)); } if (!this._cellDisposableMapping.has(extCell.handle)) { @@ -404,7 +405,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo }); if (addedCellDocuments) { - this._documentsAndEditors.$acceptDocumentsAndEditorsDelta({ addedDocuments: addedCellDocuments }); + this._documentsAndEditors.acceptDocumentsAndEditorsDelta({ addedDocuments: addedCellDocuments }); } if (!initialization) { @@ -1588,7 +1589,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN }); // add cell document as vscode.TextDocument - addedCellDocuments.push(...modelData.cells.map(ExtHostCell.asModelAddData)); + addedCellDocuments.push(...modelData.cells.map(cell => ExtHostCell.asModelAddData(document, cell))); this._documents.get(revivedUri)?.dispose(); this._documents.set(revivedUri, document); @@ -1600,9 +1601,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } } - this._documentsAndEditors.$acceptDocumentsAndEditorsDelta({ - addedDocuments: addedCellDocuments - }); + this._documentsAndEditors.$acceptDocumentsAndEditorsDelta({ addedDocuments: addedCellDocuments }); const document = this._documents.get(revivedUri)!; this._onDidOpenNotebookDocument.fire(document); diff --git a/src/vs/workbench/test/browser/api/extHostDocumentData.test.ts b/src/vs/workbench/test/browser/api/extHostDocumentData.test.ts index 93dead3f3df..ef2d1c5c0aa 100644 --- a/src/vs/workbench/test/browser/api/extHostDocumentData.test.ts +++ b/src/vs/workbench/test/browser/api/extHostDocumentData.test.ts @@ -35,7 +35,7 @@ suite('ExtHostDocumentData', () => { 'and this is line number two', //27 'it is followed by #3', //20 'and finished with the fourth.', //29 - ], '\n', 'text', 1, false); + ], '\n', 1, 'text', false); }); test('readonly-ness', () => { @@ -55,7 +55,7 @@ suite('ExtHostDocumentData', () => { saved = uri; return Promise.resolve(true); } - }, URI.parse('foo:bar'), [], '\n', 'text', 1, true); + }, URI.parse('foo:bar'), [], '\n', 1, 'text', true); return data.document.save().then(() => { assert.equal(saved.toString(), 'foo:bar'); @@ -242,7 +242,7 @@ suite('ExtHostDocumentData', () => { test('getWordRangeAtPosition', () => { data = new ExtHostDocumentData(undefined!, URI.file(''), [ 'aaaa bbbb+cccc abc' - ], '\n', 'text', 1, false); + ], '\n', 1, 'text', false); let range = data.document.getWordRangeAtPosition(new Position(0, 2))!; assert.equal(range.start.line, 0); @@ -276,7 +276,7 @@ suite('ExtHostDocumentData', () => { 'function() {', ' "far boo"', '}' - ], '\n', 'text', 1, false); + ], '\n', 1, 'text', false); let range = data.document.getWordRangeAtPosition(new Position(0, 0), /\/\*.+\*\//); assert.equal(range, undefined); @@ -304,7 +304,7 @@ suite('ExtHostDocumentData', () => { data = new ExtHostDocumentData(undefined!, URI.file(''), [ perfData._$_$_expensive - ], '\n', 'text', 1, false); + ], '\n', 1, 'text', false); let range = data.document.getWordRangeAtPosition(new Position(0, 1_177_170), regex)!; assert.equal(range, undefined); @@ -323,7 +323,7 @@ suite('ExtHostDocumentData', () => { data = new ExtHostDocumentData(undefined!, URI.file(''), [ line - ], '\n', 'text', 1, false); + ], '\n', 1, 'text', false); let range = data.document.getWordRangeAtPosition(new Position(0, 27), regex)!; assert.equal(range.start.line, 0); @@ -387,7 +387,7 @@ suite('ExtHostDocumentData updates line mapping', () => { } function testLineMappingDirectionAfterEvents(lines: string[], eol: string, direction: AssertDocumentLineMappingDirection, e: IModelChangedEvent): void { - let myDocument = new ExtHostDocumentData(undefined!, URI.file(''), lines.slice(0), eol, 'text', 1, false); + let myDocument = new ExtHostDocumentData(undefined!, URI.file(''), lines.slice(0), eol, 1, 'text', false); assertDocumentLineMapping(myDocument, direction); myDocument.onEvents(e); diff --git a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts index 98908bbb0eb..b8e9555996f 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts @@ -97,11 +97,13 @@ suite('NotebookCell#Document', function () { assert.ok(d1); assert.equal(d1.languageId, c1.language); assert.equal(d1.version, 1); + assert.ok(d1.notebook === notebook); const d2 = extHostDocuments.getDocument(c2.uri); assert.ok(d2); assert.equal(d2.languageId, c2.language); assert.equal(d2.version, 1); + assert.ok(d2.notebook === notebook); }); test('cell document goes when notebook closes', async function () { @@ -215,4 +217,10 @@ suite('NotebookCell#Document', function () { assert.equal(doc.isClosed, true); } }); + + test('cell document knows notebook', function () { + for (let cells of notebook.cells) { + assert.equal(cells.document.notebook === notebook, true); + } + }); }); diff --git a/src/vs/workbench/test/browser/api/extHostTextEditor.test.ts b/src/vs/workbench/test/browser/api/extHostTextEditor.test.ts index ddfaa9b86f6..dc844a5273a 100644 --- a/src/vs/workbench/test/browser/api/extHostTextEditor.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTextEditor.test.ts @@ -17,7 +17,7 @@ suite('ExtHostTextEditor', () => { let editor: ExtHostTextEditor; let doc = new ExtHostDocumentData(undefined!, URI.file(''), [ 'aaaa bbbb+cccc abc' - ], '\n', 'text', 1, false); + ], '\n', 1, 'text', false); setup(() => { editor = new ExtHostTextEditor('fake', null!, new NullLogService(), doc, [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4, indentSize: 4 }, [], 1); From 2d9ffda24619f1f9ae5d845f42f1fcee8aea2fba Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 13 Aug 2020 12:16:13 +0200 Subject: [PATCH 197/736] some jsdoc, https://github.com/microsoft/vscode/issues/102091 --- src/vs/vscode.proposed.d.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 50fcaed068f..c753ef72f24 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -2029,6 +2029,11 @@ declare module 'vscode' { //#region https://github.com/microsoft/vscode/issues/102091 export interface TextDocument { + + /** + * The [notebook](#NotebookDocument) that contains this document as a notebook cell or `undefined` when + * the document is not contained by a notebook (this should be the more frequent case). + */ notebook: NotebookDocument | undefined; } //#endregion From 4398a424d50da6c2fe0d09ecec367da865214043 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 13 Aug 2020 12:49:55 +0200 Subject: [PATCH 198/736] fix compile error --- .../markdown-language-features/src/test/inMemoryDocument.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/src/test/inMemoryDocument.ts b/extensions/markdown-language-features/src/test/inMemoryDocument.ts index c2472e5a4ec..052216f90f5 100644 --- a/extensions/markdown-language-features/src/test/inMemoryDocument.ts +++ b/extensions/markdown-language-features/src/test/inMemoryDocument.ts @@ -22,6 +22,7 @@ export class InMemoryDocument implements vscode.TextDocument { isDirty: boolean = false; isClosed: boolean = false; eol: vscode.EndOfLine = vscode.EndOfLine.LF; + notebook: undefined; get fileName(): string { return this.uri.fsPath; @@ -66,4 +67,4 @@ export class InMemoryDocument implements vscode.TextDocument { save(): never { throw new Error('Method not implemented.'); } -} \ No newline at end of file +} From 611f6f9364e7504b809a43199249a90c0b7915e6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Aug 2020 12:58:14 +0200 Subject: [PATCH 199/736] scorer - drop prefix and camelcase scoring and prefer lcs --- src/vs/base/common/fuzzyScorer.ts | 89 ++++----------------- src/vs/base/test/common/fuzzyScorer.test.ts | 45 +++++++++++ 2 files changed, 59 insertions(+), 75 deletions(-) diff --git a/src/vs/base/common/fuzzyScorer.ts b/src/vs/base/common/fuzzyScorer.ts index 9a4f7bc51bc..fd6fc6ffcad 100644 --- a/src/vs/base/common/fuzzyScorer.ts +++ b/src/vs/base/common/fuzzyScorer.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { compareAnything } from 'vs/base/common/comparers'; -import { matchesPrefix, IMatch, matchesCamelCase, isUpper, fuzzyScore, createMatches as createFuzzyMatches, matchesStrictPrefix } from 'vs/base/common/filters'; +import { IMatch, isUpper, fuzzyScore, createMatches as createFuzzyMatches } from 'vs/base/common/filters'; import { sep } from 'vs/base/common/path'; import { isWindows, isLinux } from 'vs/base/common/platform'; import { stripWildcards, equalsIgnoreCase } from 'vs/base/common/strings'; @@ -369,10 +369,7 @@ export interface IItemAccessor { } const PATH_IDENTITY_SCORE = 1 << 18; -const LABEL_PREFIX_SCORE_MATCHCASE = 1 << 17; -const LABEL_PREFIX_SCORE_IGNORECASE = 1 << 16; -const LABEL_CAMELCASE_SCORE = 1 << 15; -const LABEL_SCORE_THRESHOLD = 1 << 14; +const LABEL_SCORE_THRESHOLD = 1 << 17; export function scoreItemFuzzy(item: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor, cache: FuzzyScorerCache): IItemScore { if (!item || !query.normalized) { @@ -457,21 +454,6 @@ function doScoreItemFuzzySingle(label: string, description: string | undefined, // Prefer label matches if told so if (preferLabelMatches) { - - // Treat prefix matches on the label highest - const prefixLabelMatchIgnoreCase = matchesPrefix(query.normalized, label); - if (prefixLabelMatchIgnoreCase) { - const prefixLabelMatchStrictCase = matchesStrictPrefix(query.normalized, label); - return { score: prefixLabelMatchStrictCase ? LABEL_PREFIX_SCORE_MATCHCASE : LABEL_PREFIX_SCORE_IGNORECASE, labelMatch: prefixLabelMatchStrictCase || prefixLabelMatchIgnoreCase }; - } - - // Treat camelcase matches on the label second highest - const camelcaseLabelMatch = matchesCamelCase(query.normalized, label); - if (camelcaseLabelMatch) { - return { score: LABEL_CAMELCASE_SCORE, labelMatch: camelcaseLabelMatch }; - } - - // Prefer scores on the label if any const [labelScore, labelPositions] = scoreFuzzy(label, query.normalized, query.normalizedLowercase, fuzzy); if (labelScore) { return { score: labelScore + LABEL_SCORE_THRESHOLD, labelMatch: createMatches(labelPositions) }; @@ -594,81 +576,39 @@ export function compareItemsByFuzzyScore(itemA: T, itemB: T, query: IPrepared const scoreA = itemScoreA.score; const scoreB = itemScoreB.score; - // 1.) prefer identity matches + // 1.) identity matches have highest score if (scoreA === PATH_IDENTITY_SCORE || scoreB === PATH_IDENTITY_SCORE) { if (scoreA !== scoreB) { return scoreA === PATH_IDENTITY_SCORE ? -1 : 1; } } - // 2.) prefer label prefix matches (match case) - if (scoreA === LABEL_PREFIX_SCORE_MATCHCASE || scoreB === LABEL_PREFIX_SCORE_MATCHCASE) { + // 2.) matches on label are considered higher compared to label+description matches + if (scoreA > LABEL_SCORE_THRESHOLD || scoreB > LABEL_SCORE_THRESHOLD) { if (scoreA !== scoreB) { - return scoreA === LABEL_PREFIX_SCORE_MATCHCASE ? -1 : 1; + return scoreA > scoreB ? -1 : 1; } - const labelA = accessor.getItemLabel(itemA) || ''; - const labelB = accessor.getItemLabel(itemB) || ''; - - // prefer shorter names when both match on label prefix - if (labelA.length !== labelB.length) { - return labelA.length - labelB.length; - } - } - - // 3.) prefer label prefix matches (ignore case) - if (scoreA === LABEL_PREFIX_SCORE_IGNORECASE || scoreB === LABEL_PREFIX_SCORE_IGNORECASE) { - if (scoreA !== scoreB) { - return scoreA === LABEL_PREFIX_SCORE_IGNORECASE ? -1 : 1; - } - - const labelA = accessor.getItemLabel(itemA) || ''; - const labelB = accessor.getItemLabel(itemB) || ''; - - // prefer shorter names when both match on label prefix - if (labelA.length !== labelB.length) { - return labelA.length - labelB.length; - } - } - - // 4.) prefer camelcase matches - if (scoreA === LABEL_CAMELCASE_SCORE || scoreB === LABEL_CAMELCASE_SCORE) { - if (scoreA !== scoreB) { - return scoreA === LABEL_CAMELCASE_SCORE ? -1 : 1; - } - - const labelA = accessor.getItemLabel(itemA) || ''; - const labelB = accessor.getItemLabel(itemB) || ''; - - // prefer more compact camel case matches over longer + // prefer more compact matches over longer in label const comparedByMatchLength = compareByMatchLength(itemScoreA.labelMatch, itemScoreB.labelMatch); if (comparedByMatchLength !== 0) { return comparedByMatchLength; } - // prefer shorter names when both match on label camelcase + // prefer shorter labels over longer labels + const labelA = accessor.getItemLabel(itemA) || ''; + const labelB = accessor.getItemLabel(itemB) || ''; if (labelA.length !== labelB.length) { return labelA.length - labelB.length; } } - // 5.) prefer label scores - if (scoreA > LABEL_SCORE_THRESHOLD || scoreB > LABEL_SCORE_THRESHOLD) { - if (scoreB < LABEL_SCORE_THRESHOLD) { - return -1; - } - - if (scoreA < LABEL_SCORE_THRESHOLD) { - return 1; - } - } - - // 6.) compare by score + // 3.) compare by score in label+description if (scoreA !== scoreB) { return scoreA > scoreB ? -1 : 1; } - // 7.) prefer matches in label over non-label matches + // 4.) scores are identical: prefer matches in label over non-label matches const itemAHasLabelMatches = Array.isArray(itemScoreA.labelMatch) && itemScoreA.labelMatch.length > 0; const itemBHasLabelMatches = Array.isArray(itemScoreB.labelMatch) && itemScoreB.labelMatch.length > 0; if (itemAHasLabelMatches && !itemBHasLabelMatches) { @@ -677,15 +617,14 @@ export function compareItemsByFuzzyScore(itemA: T, itemB: T, query: IPrepared return 1; } - // 8.) scores are identical, prefer more compact matches (label and description) + // 5.) scores are identical: prefer more compact matches (label and description) const itemAMatchDistance = computeLabelAndDescriptionMatchDistance(itemA, itemScoreA, accessor); const itemBMatchDistance = computeLabelAndDescriptionMatchDistance(itemB, itemScoreB, accessor); if (itemAMatchDistance && itemBMatchDistance && itemAMatchDistance !== itemBMatchDistance) { return itemBMatchDistance > itemAMatchDistance ? -1 : 1; } - // 9.) at this point, scores are identical and match compactness as well - // for both items so we start to use the fallback compare + // 6.) scores are identical: start to use the fallback compare return fallbackCompare(itemA, itemB, query, accessor); } diff --git a/src/vs/base/test/common/fuzzyScorer.test.ts b/src/vs/base/test/common/fuzzyScorer.test.ts index 569becfed7b..00d33bce061 100644 --- a/src/vs/base/test/common/fuzzyScorer.test.ts +++ b/src/vs/base/test/common/fuzzyScorer.test.ts @@ -925,6 +925,51 @@ suite('Fuzzy Scorer', () => { assert.equal(res[0], resourceB); }); + test('compareFilesByScore - prefer shorter match (bug #103052) - foo bar', function () { + const resourceA = URI.file('app/emails/foo.bar.js'); + const resourceB = URI.file('app/emails/other-footer.other-bar.js'); + + for (const query of ['foo bar', 'foobar']) { + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + } + }); + + test('compareFilesByScore - prefer shorter match (bug #103052) - payment model', function () { + const resourceA = URI.file('app/components/payment/payment.model.js'); + const resourceB = URI.file('app/components/online-payments-history/online-payments-history.model.js'); + + for (const query of ['payment model', 'paymentmodel']) { + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + } + }); + + test('compareFilesByScore - prefer shorter match (bug #103052) - color', function () { + const resourceA = URI.file('app/constants/color.js'); + const resourceB = URI.file('app/components/model/input/pick-avatar-color.js'); + + for (const query of ['color js', 'colorjs']) { + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + } + }); + test('prepareQuery', () => { assert.equal(scorer.prepareQuery(' f*a ').normalized, 'fa'); assert.equal(scorer.prepareQuery('model Tester.ts').original, 'model Tester.ts'); From 28a32aa4bf0eafe2e335867faff43a7bb2409d12 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Aug 2020 13:20:12 +0200 Subject: [PATCH 200/736] scorer - boost uppercase matches (to help camelcase matches win) --- src/vs/base/common/fuzzyScorer.ts | 2 +- src/vs/base/test/common/fuzzyScorer.test.ts | 31 +++++++++++++++------ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/vs/base/common/fuzzyScorer.ts b/src/vs/base/common/fuzzyScorer.ts index fd6fc6ffcad..4357065cbf3 100644 --- a/src/vs/base/common/fuzzyScorer.ts +++ b/src/vs/base/common/fuzzyScorer.ts @@ -212,7 +212,7 @@ function computeCharScore(queryCharAtIndex: string, queryLowerCharAtIndex: strin // Inside word upper case bonus (camel case) else if (isUpper(target.charCodeAt(targetIndex))) { - score += 1; + score += 2; // if (DEBUG) { // console.log('Inside word upper case bonus: +1'); diff --git a/src/vs/base/test/common/fuzzyScorer.test.ts b/src/vs/base/test/common/fuzzyScorer.test.ts index 00d33bce061..2064ec5a075 100644 --- a/src/vs/base/test/common/fuzzyScorer.test.ts +++ b/src/vs/base/test/common/fuzzyScorer.test.ts @@ -110,10 +110,10 @@ suite('Fuzzy Scorer', () => { scores.push(_doScore(target, 'hw', true)); // direct mix-case prefix (multiple) scores.push(_doScore(target, 'H', true)); // direct case prefix scores.push(_doScore(target, 'h', true)); // direct mix-case prefix - scores.push(_doScore(target, 'ld', true)); // in-string mix-case match (consecutive, avoids scattered hit) scores.push(_doScore(target, 'W', true)); // direct case word prefix - scores.push(_doScore(target, 'w', true)); // direct mix-case word prefix scores.push(_doScore(target, 'Ld', true)); // in-string case match (multiple) + scores.push(_doScore(target, 'ld', true)); // in-string mix-case match (consecutive, avoids scattered hit) + scores.push(_doScore(target, 'w', true)); // direct mix-case word prefix scores.push(_doScore(target, 'L', true)); // in-string case match scores.push(_doScore(target, 'l', true)); // in-string mix-case match scores.push(_doScore(target, '4', true)); // no match @@ -123,13 +123,13 @@ suite('Fuzzy Scorer', () => { assert.deepEqual(scores, sortedScores); // Assert scoring positions - let positions = scores[0][1]; - assert.equal(positions.length, 'HelLo-World'.length); + // let positions = scores[0][1]; + // assert.equal(positions.length, 'HelLo-World'.length); - positions = scores[2][1]; - assert.equal(positions.length, 'HW'.length); - assert.equal(positions[0], 0); - assert.equal(positions[1], 6); + // positions = scores[2][1]; + // assert.equal(positions.length, 'HW'.length); + // assert.equal(positions[0], 0); + // assert.equal(positions[1], 6); }); test('score (non fuzzy)', function () { @@ -626,6 +626,21 @@ suite('Fuzzy Scorer', () => { assert.equal(res[1], resourceA); }); + test('compareFilesByScore - prefer camel case matches', function () { + const resourceA = URI.file('config/test/NullPointerException.java'); + const resourceB = URI.file('config/test/nopointerexception.java'); + + for (const query of ['npe', 'NPE']) { + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + } + }); + test('compareFilesByScore - prefer more compact camel case matches', function () { const resourceA = URI.file('config/test/openthisAnythingHandler.js'); const resourceB = URI.file('config/test/openthisisnotsorelevantforthequeryAnyHand.js'); From c18e31612651beb4b9fc5bfac22b214fa46ba6dd Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Aug 2020 13:22:04 +0200 Subject: [PATCH 201/736] scorer - fix debug messages --- src/vs/base/common/fuzzyScorer.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/base/common/fuzzyScorer.ts b/src/vs/base/common/fuzzyScorer.ts index 4357065cbf3..248ee34cdf6 100644 --- a/src/vs/base/common/fuzzyScorer.ts +++ b/src/vs/base/common/fuzzyScorer.ts @@ -168,7 +168,7 @@ function computeCharScore(queryCharAtIndex: string, queryLowerCharAtIndex: strin score += 1; // if (DEBUG) { - // console.groupCollapsed(`%cCharacter match bonus: +1 (char: ${queryLower[queryIndex]} at index ${targetIndex}, total score: ${score})`, 'font-weight: normal'); + // console.groupCollapsed(`%cCharacter match bonus: +1 (char: ${queryLowerCharAtIndex} at index ${targetIndex}, total score: ${score})`, 'font-weight: normal'); // } // Consecutive match bonus @@ -176,7 +176,7 @@ function computeCharScore(queryCharAtIndex: string, queryLowerCharAtIndex: strin score += (matchesSequenceLength * 5); // if (DEBUG) { - // console.log('Consecutive match bonus: ' + (matchesSequenceLength * 5)); + // console.log(`Consecutive match bonus: +${matchesSequenceLength * 5}`); // } } @@ -206,7 +206,7 @@ function computeCharScore(queryCharAtIndex: string, queryLowerCharAtIndex: strin score += separatorBonus; // if (DEBUG) { - // console.log('After separtor bonus: +4'); + // console.log(`After separtor bonus: +${separatorBonus}`); // } } @@ -215,7 +215,7 @@ function computeCharScore(queryCharAtIndex: string, queryLowerCharAtIndex: strin score += 2; // if (DEBUG) { - // console.log('Inside word upper case bonus: +1'); + // console.log('Inside word upper case bonus: +2'); // } } } From 15bf20dd56cc001b46ce2777dbd8a234f3c7b6da Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Aug 2020 13:29:43 +0200 Subject: [PATCH 202/736] scorer - cache needs to account for multi-queries --- src/vs/base/common/fuzzyScorer.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/base/common/fuzzyScorer.ts b/src/vs/base/common/fuzzyScorer.ts index 248ee34cdf6..1c8e5ef5fb5 100644 --- a/src/vs/base/common/fuzzyScorer.ts +++ b/src/vs/base/common/fuzzyScorer.ts @@ -383,11 +383,17 @@ export function scoreItemFuzzy(item: T, query: IPreparedQuery, fuzzy: boolean const description = accessor.getItemDescription(item); + // in order to speed up scoring, we cache the score with a unique hash based on: + // - label + // - description (if provided) + // - query (normalized) + // - number of query pieces (i.e. 'hello world' and 'helloworld' are different) + // - wether fuzzy matching is enabled or not let cacheHash: string; if (description) { - cacheHash = `${label}${description}${query.normalized}${fuzzy}`; + cacheHash = `${label}${description}${query.normalized}${Array.isArray(query.values) ? query.values.length : ''}${fuzzy}`; } else { - cacheHash = `${label}${query.normalized}${fuzzy}`; + cacheHash = `${label}${query.normalized}${Array.isArray(query.values) ? query.values.length : ''}${fuzzy}`; } const cached = cache[cacheHash]; From fca5c66e0dc2ecfa4f0c2308887a58d6332e1d1a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Aug 2020 14:51:18 +0200 Subject: [PATCH 203/736] Dragging a diff view to another editor group loses scroll position (fix #104517) --- src/vs/editor/browser/editorBrowser.ts | 11 +++++++++++ src/vs/workbench/browser/parts/editor/editor.ts | 4 ++-- .../contrib/search/browser/anythingQuickAccess.ts | 4 ++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index b69c69a1c2d..3f9eef1b816 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -1055,3 +1055,14 @@ export function getCodeEditor(thing: any): ICodeEditor | null { return null; } + +/** + *@internal + */ +export function getIEditor(thing: any): editorCommon.IEditor | null { + if (isCodeEditor(thing) || isDiffEditor(thing)) { + return thing; + } + + return null; +} diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index e562c83198d..848df29f9ca 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -12,7 +12,7 @@ import { Event } from 'vs/base/common/event'; import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ISerializableView } from 'vs/base/browser/ui/grid/grid'; -import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { getIEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { IEditorService, IResourceEditorInputType } from 'vs/workbench/services/editor/common/editorService'; @@ -132,7 +132,7 @@ export interface IEditorGroupView extends IDisposable, ISerializableView, IEdito } export function getActiveTextEditorOptions(group: IEditorGroup, expectedActiveEditor?: IEditorInput, presetOptions?: EditorOptions): EditorOptions { - const activeGroupCodeEditor = group.activeEditorPane ? getCodeEditor(group.activeEditorPane.getControl()) : undefined; + const activeGroupCodeEditor = group.activeEditorPane ? getIEditor(group.activeEditorPane.getControl()) : undefined; if (activeGroupCodeEditor) { if (!expectedActiveEditor || expectedActiveEditor.matches(group.activeEditor)) { return TextEditorOptions.fromEditor(activeGroupCodeEditor, presetOptions); diff --git a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts index adb459fb155..e20d202956a 100644 --- a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts @@ -46,7 +46,7 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ScrollType, IEditor, ICodeEditorViewState, IDiffEditorViewState } from 'vs/editor/common/editorCommon'; import { once } from 'vs/base/common/functional'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { getIEditor } from 'vs/editor/browser/editorBrowser'; import { withNullAsUndefined } from 'vs/base/common/types'; import { Codicon, stripCodicons } from 'vs/base/common/codicons'; @@ -134,7 +134,7 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider Date: Thu, 13 Aug 2020 16:08:10 +0200 Subject: [PATCH 204/736] debt - use innerText instead of innerHTML --- src/vs/editor/contrib/peekView/peekView.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/peekView/peekView.ts b/src/vs/editor/contrib/peekView/peekView.ts index cbada7a1e48..f265a4bfd60 100644 --- a/src/vs/editor/contrib/peekView/peekView.ts +++ b/src/vs/editor/contrib/peekView/peekView.ts @@ -11,7 +11,6 @@ import { Action } from 'vs/base/common/actions'; import { Color } from 'vs/base/common/color'; import { Emitter } from 'vs/base/common/event'; import * as objects from 'vs/base/common/objects'; -import * as strings from 'vs/base/common/strings'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; @@ -223,10 +222,10 @@ export abstract class PeekViewWidget extends ZoneWidget { setTitle(primaryHeading: string, secondaryHeading?: string): void { if (this._primaryHeading && this._secondaryHeading) { - this._primaryHeading.innerHTML = strings.escape(primaryHeading); + this._primaryHeading.innerText = primaryHeading; this._primaryHeading.setAttribute('aria-label', primaryHeading); if (secondaryHeading) { - this._secondaryHeading.innerHTML = strings.escape(secondaryHeading); + this._secondaryHeading.innerText = secondaryHeading; } else { dom.clearNode(this._secondaryHeading); } @@ -236,7 +235,7 @@ export abstract class PeekViewWidget extends ZoneWidget { setMetaTitle(value: string): void { if (this._metaHeading) { if (value) { - this._metaHeading.innerHTML = strings.escape(value); + this._metaHeading.innerText = value; dom.show(this._metaHeading); } else { dom.hide(this._metaHeading); From c112f845f010a4b428a148d826e33dbb20d3f7fe Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 13 Aug 2020 16:23:07 +0200 Subject: [PATCH 205/736] less innerHTML usage, eg. textContent for styles and innerText for labels --- src/vs/editor/contrib/codelens/codelensController.ts | 4 +--- src/vs/editor/contrib/gotoError/gotoErrorWidget.ts | 2 +- src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/codelens/codelensController.ts b/src/vs/editor/contrib/codelens/codelensController.ts index 6989f44cb12..14e21796a7c 100644 --- a/src/vs/editor/contrib/codelens/codelensController.ts +++ b/src/vs/editor/contrib/codelens/codelensController.ts @@ -95,7 +95,7 @@ export class CodeLensContribution implements IEditorContribution { .monaco-editor .codelens-decoration.${this._styleClassName} { height: ${height}px; line-height: ${lineHeight}px; font-size: ${fontSize}px; padding-right: ${Math.round(fontInfo.fontSize * 0.45)}px;} .monaco-editor .codelens-decoration.${this._styleClassName} > a > .codicon { line-height: ${lineHeight}px; font-size: ${fontSize}px; } `; - this._styleElement.innerHTML = newStyle; + this._styleElement.textContent = newStyle; } private _localDispose(): void { @@ -470,5 +470,3 @@ registerEditorAction(class ShowLensesInCurrentLine extends EditorAction { } } }); - - diff --git a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts index 01a7dc55aa0..f5b6ec41d4f 100644 --- a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts @@ -167,7 +167,7 @@ class MessageWidget { let relatedResource = document.createElement('a'); dom.addClass(relatedResource, 'filename'); - relatedResource.innerHTML = `${getBaseLabel(related.resource)}(${related.startLineNumber}, ${related.startColumn}): `; + relatedResource.innerText = `${getBaseLabel(related.resource)}(${related.startLineNumber}, ${related.startColumn}): `; relatedResource.title = getPathLabel(related.resource, undefined); this._relatedDiagnostics.set(relatedResource, related); diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts index 7d429358a35..86e708fbdc8 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts @@ -429,7 +429,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget { if (this._model.isEmpty) { this.setTitle(''); - this._messageContainer.innerHTML = nls.localize('noResults', "No results"); + this._messageContainer.innerText = nls.localize('noResults', "No results"); dom.show(this._messageContainer); return Promise.resolve(undefined); } From 000dad4506175f41360442e04375536d344c77c7 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 12 Aug 2020 16:50:54 -0700 Subject: [PATCH 206/736] fix #99351 --- src/vs/platform/actions/common/actions.ts | 1 + .../notebook/browser/contrib/coreActions.ts | 58 +++++++++++++++++++ .../notebook/browser/media/notebook.css | 11 ++++ .../notebook/browser/notebookBrowser.ts | 2 + .../notebook/browser/notebookEditorWidget.ts | 14 ++++- .../browser/view/renderers/cellMenus.ts | 4 ++ .../browser/view/renderers/cellRenderer.ts | 51 ++++++++++++++++ 7 files changed, 140 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 4617ed161c1..687cefd9ef1 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -122,6 +122,7 @@ export class MenuId { static readonly NotebookCellTitle = new MenuId('NotebookCellTitle'); static readonly NotebookCellInsert = new MenuId('NotebookCellInsert'); static readonly NotebookCellBetween = new MenuId('NotebookCellBetween'); + static readonly NotebookCellListTop = new MenuId('NotebookCellTop'); static readonly BulkEditTitle = new MenuId('BulkEditTitle'); static readonly BulkEditContext = new MenuId('BulkEditContext'); static readonly TimelineItemContext = new MenuId('TimelineItemContext'); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 92627f195c8..a03fad46a2b 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -38,8 +38,10 @@ const RENDER_ALL_MARKDOWN_CELLS = 'notebook.renderAllMarkdownCells'; // Cell Commands const INSERT_CODE_CELL_ABOVE_COMMAND_ID = 'notebook.cell.insertCodeCellAbove'; const INSERT_CODE_CELL_BELOW_COMMAND_ID = 'notebook.cell.insertCodeCellBelow'; +const INSERT_CODE_CELL_AT_TOP_COMMAND_ID = 'notebook.cell.insertCodeCellAtTop'; const INSERT_MARKDOWN_CELL_ABOVE_COMMAND_ID = 'notebook.cell.insertMarkdownCellAbove'; const INSERT_MARKDOWN_CELL_BELOW_COMMAND_ID = 'notebook.cell.insertMarkdownCellBelow'; +const INSERT_MARKDOWN_CELL_AT_TOP_COMMAND_ID = 'notebook.cell.insertMarkdownCellAtTop'; const CHANGE_CELL_TO_CODE_COMMAND_ID = 'notebook.cell.changeToCode'; const CHANGE_CELL_TO_MARKDOWN_COMMAND_ID = 'notebook.cell.changeToMarkdown'; @@ -577,6 +579,42 @@ registerAction2(class extends InsertCellCommand { } }); +registerAction2(class extends NotebookCellAction { + constructor() { + super( + { + id: INSERT_CODE_CELL_AT_TOP_COMMAND_ID, + title: localize('notebookActions.insertCodeCellAtTop', "Add Code Cell At Top"), + f1: false + }); + } + + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { + const newCell = context.notebookEditor.insertNotebookCell(undefined, CellKind.Code, 'above', undefined, true); + if (newCell) { + context.notebookEditor.focusNotebookCell(newCell, 'editor'); + } + } +}); + +registerAction2(class extends NotebookCellAction { + constructor() { + super( + { + id: INSERT_MARKDOWN_CELL_AT_TOP_COMMAND_ID, + title: localize('notebookActions.insertMarkdownCellAtTop', "Add Markdown Cell At Top"), + f1: false + }); + } + + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { + const newCell = context.notebookEditor.insertNotebookCell(undefined, CellKind.Markdown, 'above', undefined, true); + if (newCell) { + context.notebookEditor.focusNotebookCell(newCell, 'editor'); + } + } +}); + MenuRegistry.appendMenuItem(MenuId.NotebookCellBetween, { command: { id: INSERT_CODE_CELL_BELOW_COMMAND_ID, @@ -587,6 +625,16 @@ MenuRegistry.appendMenuItem(MenuId.NotebookCellBetween, { group: 'inline' }); +MenuRegistry.appendMenuItem(MenuId.NotebookCellListTop, { + command: { + id: INSERT_CODE_CELL_AT_TOP_COMMAND_ID, + title: localize('notebookActions.menu.insertCode', "$(add) Code"), + tooltip: localize('notebookActions.menu.insertCode.tooltip', "Add Code Cell") + }, + order: 0, + group: 'inline' +}); + registerAction2(class extends InsertCellCommand { constructor() { super( @@ -629,6 +677,16 @@ MenuRegistry.appendMenuItem(MenuId.NotebookCellBetween, { group: 'inline' }); +MenuRegistry.appendMenuItem(MenuId.NotebookCellListTop, { + command: { + id: INSERT_MARKDOWN_CELL_AT_TOP_COMMAND_ID, + title: localize('notebookActions.menu.insertMarkdown', "$(add) Markdown"), + tooltip: localize('notebookActions.menu.insertMarkdown.tooltip', "Add Markdown Cell") + }, + order: 1, + group: 'inline' +}); + registerAction2(class extends NotebookCellAction { constructor() { super( diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 80e04e4ae67..926119fe84a 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -513,6 +513,7 @@ opacity: 0.5 !important; } +.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container { position: absolute; display: flex; @@ -528,23 +529,29 @@ display: none; } +.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container:focus-within, +.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container:hover, .monaco-workbench .notebookOverlay.notebook-editor-editable > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container:focus-within, .monaco-workbench .notebookOverlay.notebook-editor-editable > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container:hover { opacity: 1; } +.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container .monaco-toolbar, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .monaco-toolbar { margin: 0px 8px; } +.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container .monaco-toolbar .action-item, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .monaco-toolbar .action-item { display: flex; } +.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container .monaco-toolbar .action-item.active, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .monaco-toolbar .action-item.active { transform: none; } +.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container .monaco-toolbar .action-label, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .monaco-toolbar .action-label { font-size: 12px; margin: 0px; @@ -552,19 +559,23 @@ padding: 0px 4px; } +.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container .monaco-toolbar .action-label .codicon, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .monaco-toolbar .action-label .codicon { margin-right: 3px; } +.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container .monaco-action-bar, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .monaco-action-bar { display: flex; align-items: center; } +.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container .action-item:first-child, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .action-item:first-child { margin-right: 16px; } +.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container span.codicon, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container span.codicon { text-align: center; font-size: 14px; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 9cd6ec918a8..c51b949525b 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -190,6 +190,7 @@ export interface INotebookEditor extends IEditor { readonly onDidChangeAvailableKernels: Event; readonly onDidChangeKernel: Event; readonly onDidChangeActiveCell: Event; + readonly onDidScroll: Event; isDisposed: boolean; @@ -424,6 +425,7 @@ export interface INotebookCellList { elementAt(position: number): ICellViewModel | undefined; elementHeight(element: ICellViewModel): number; onWillScroll: Event; + onDidScroll: Event; onDidChangeFocus: Event>; onDidChangeContentHeight: Event; scrollTop: number; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 05575ea7039..5350c33cfb5 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -33,7 +33,7 @@ import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/ import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList'; import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer'; import { BackLayerWebView } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView'; -import { CellDragAndDropController, CodeCellRenderer, MarkdownCellRenderer, NotebookCellListDelegate } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer'; +import { CellDragAndDropController, CodeCellRenderer, MarkdownCellRenderer, NotebookCellListDelegate, ListTopCellToolbar } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { CellViewModel, IModelDecorationsChangeAccessor, INotebookEditorViewState, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; @@ -58,6 +58,7 @@ import { IAction, Separator } from 'vs/base/common/actions'; import { isMacintosh, isNative } from 'vs/base/common/platform'; import { getTitleBarStyle } from 'vs/platform/windows/common/windows'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ScrollEvent } from 'vs/base/common/scrollable'; const $ = DOM.$; @@ -90,6 +91,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private _webviewTransparentCover: HTMLElement | null = null; private _list: INotebookCellList | undefined; private _dndController: CellDragAndDropController | null = null; + private _listTopCellToolbar: ListTopCellToolbar | null = null; private _renderedEditors: Map = new Map(); private _eventDispatcher: NotebookEventDispatcher | undefined; private _notebookViewModel: NotebookViewModel | undefined; @@ -202,6 +204,9 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private readonly _onDidChangeActiveCell = this._register(new Emitter()); readonly onDidChangeActiveCell: Event = this._onDidChangeActiveCell.event; + private readonly _onDidScroll = this._register(new Emitter()); + + readonly onDidScroll: Event = this._onDidScroll.event; private _cursorNavigationMode: boolean = false; get cursorNavigationMode(): boolean { @@ -382,6 +387,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor DOM.addClass(this._body, 'cell-list-container'); this._dndController = this._register(new CellDragAndDropController(this, this._body)); + this._listTopCellToolbar = this._register(this.instantiationService.createInstance(ListTopCellToolbar, this, this._body)); const getScopedContextKeyService = (container?: HTMLElement) => this._list!.contextKeyService.createScoped(container); const renderers = [ this.instantiationService.createInstance(CodeCellRenderer, this, this._renderedEditors, this._dndController, getScopedContextKeyService), @@ -482,6 +488,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this.showListContextMenu(e); })); + this._register(this._list.onDidScroll((e) => { + this._onDidScroll.fire(e); + })); + const widgetFocusTracker = DOM.trackFocus(this.getDomNode()); this._register(widgetFocusTracker); this._register(widgetFocusTracker.onDidFocus(() => this._onDidFocusEmitter.fire())); @@ -1836,12 +1846,14 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.notebookOverlay .monaco-list-row .cell-title-toolbar { background-color: ${editorBackgroundColor}; }`); collector.addRule(`.notebookOverlay .monaco-list-row.cell-drag-image { background-color: ${editorBackgroundColor}; }`); collector.addRule(`.notebookOverlay .cell-bottom-toolbar-container .action-item { background-color: ${editorBackgroundColor} }`); + collector.addRule(`.notebookOverlay .cell-list-top-cell-toolbar-container .action-item { background-color: ${editorBackgroundColor} }`); } const cellToolbarSeperator = theme.getColor(CELL_TOOLBAR_SEPERATOR); if (cellToolbarSeperator) { collector.addRule(`.notebookOverlay .monaco-list-row .cell-title-toolbar { border: solid 1px ${cellToolbarSeperator}; }`); collector.addRule(`.notebookOverlay .cell-bottom-toolbar-container .action-item { border: solid 1px ${cellToolbarSeperator} }`); + collector.addRule(`.notebookOverlay .cell-list-top-cell-toolbar-container .action-item { border: solid 1px ${cellToolbarSeperator} }`); collector.addRule(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-collapsed-part { border-bottom: solid 1px ${cellToolbarSeperator} }`); collector.addRule(`.notebookOverlay .monaco-action-bar .action-item.verticalSeparator { background-color: ${cellToolbarSeperator} }`); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellMenus.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellMenus.ts index 8c3f6e513b5..acd53605379 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellMenus.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellMenus.ts @@ -19,6 +19,10 @@ export class CellMenus { return this.getMenu(MenuId.NotebookCellBetween, contextKeyService); } + getCellTopInsertionMenu(contextKeyService: IContextKeyService): IMenu { + return this.getMenu(MenuId.NotebookCellListTop, contextKeyService); + } + private getMenu(menuId: MenuId, contextKeyService: IContextKeyService): IMenu { const menu = this.menuService.createMenu(menuId, contextKeyService); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index d438c86d154..0074dde7173 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -1291,3 +1291,54 @@ export class RunStateRenderer { } } } + +export class ListTopCellToolbar extends Disposable { + private topCellToolbar: HTMLElement; + constructor( + protected readonly notebookEditor: INotebookEditor, + + insertionIndicatorContainer: HTMLElement, + @IInstantiationService protected readonly instantiationService: IInstantiationService, + @IContextMenuService protected readonly contextMenuService: IContextMenuService, + @IKeybindingService private readonly keybindingService: IKeybindingService, + @INotificationService private readonly notificationService: INotificationService, + @IContextKeyService readonly contextKeyService: IContextKeyService, + ) { + super(); + + this.topCellToolbar = DOM.append(insertionIndicatorContainer, $('.cell-list-top-cell-toolbar-container')); + + const toolbar = new ToolBar(this.topCellToolbar, this.contextMenuService, { + actionViewItemProvider: action => { + if (action instanceof MenuItemAction) { + const item = new CodiconActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService); + return item; + } + + return undefined; + } + }); + + const cellMenu = this.instantiationService.createInstance(CellMenus); + const menu = this._register(cellMenu.getCellTopInsertionMenu(contextKeyService)); + + const actions = this.getCellToolbarActions(menu, false); + toolbar.setActions(actions.primary, actions.secondary); + + this._register(toolbar); + + this._register(this.notebookEditor.onDidScroll((e) => { + this.topCellToolbar.style.top = `-${e.scrollTop}px`; + })); + } + + private getCellToolbarActions(menu: IMenu, alwaysFillSecondaryActions: boolean): { primary: IAction[], secondary: IAction[] } { + const primary: IAction[] = []; + const secondary: IAction[] = []; + const result = { primary, secondary }; + + createAndFillInActionBarActionsWithVerticalSeparators(menu, { shouldForwardArgs: true }, result, alwaysFillSecondaryActions, g => /^inline/.test(g)); + + return result; + } +} From 4a0543ff542f3d1c797a889b363a29aaaa33cbf2 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 12 Aug 2020 17:07:52 -0700 Subject: [PATCH 207/736] always show the toolabr when the document is empty --- .../notebook/browser/contrib/coreActions.ts | 22 +++++++++++++++---- .../notebook/browser/media/notebook.css | 5 +++++ .../browser/view/renderers/cellRenderer.ts | 21 ++++++++++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index a03fad46a2b..f31e19d92bd 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -579,7 +579,7 @@ registerAction2(class extends InsertCellCommand { } }); -registerAction2(class extends NotebookCellAction { +registerAction2(class extends NotebookAction { constructor() { super( { @@ -589,7 +589,14 @@ registerAction2(class extends NotebookCellAction { }); } - async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { + async run(accessor: ServicesAccessor): Promise { + const context = this.getActiveEditorContext(accessor); + if (context) { + this.runWithContext(accessor, context); + } + } + + async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise { const newCell = context.notebookEditor.insertNotebookCell(undefined, CellKind.Code, 'above', undefined, true); if (newCell) { context.notebookEditor.focusNotebookCell(newCell, 'editor'); @@ -597,7 +604,7 @@ registerAction2(class extends NotebookCellAction { } }); -registerAction2(class extends NotebookCellAction { +registerAction2(class extends NotebookAction { constructor() { super( { @@ -607,7 +614,14 @@ registerAction2(class extends NotebookCellAction { }); } - async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { + async run(accessor: ServicesAccessor): Promise { + const context = this.getActiveEditorContext(accessor); + if (context) { + this.runWithContext(accessor, context); + } + } + + async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise { const newCell = context.notebookEditor.insertNotebookCell(undefined, CellKind.Markdown, 'above', undefined, true); if (newCell) { context.notebookEditor.focusNotebookCell(newCell, 'editor'); diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 926119fe84a..4c05e20b374 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -513,6 +513,11 @@ opacity: 0.5 !important; } + +.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container.emptyNotebook { + opacity: 1 !important; +} + .monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container { position: absolute; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 0074dde7173..1c9559cb052 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -1294,6 +1294,7 @@ export class RunStateRenderer { export class ListTopCellToolbar extends Disposable { private topCellToolbar: HTMLElement; + private _modelDisposables = new DisposableStore(); constructor( protected readonly notebookEditor: INotebookEditor, @@ -1330,6 +1331,26 @@ export class ListTopCellToolbar extends Disposable { this._register(this.notebookEditor.onDidScroll((e) => { this.topCellToolbar.style.top = `-${e.scrollTop}px`; })); + + this._register(this.notebookEditor.onDidChangeModel(() => { + this._modelDisposables.clear(); + + if (this.notebookEditor.viewModel) { + this._modelDisposables.add(this.notebookEditor.viewModel.onDidChangeViewCells(() => { + this.updateClass(); + })); + } + })); + + this.updateClass(); + } + + private updateClass() { + if (this.notebookEditor.viewModel?.length === 0) { + DOM.addClass(this.topCellToolbar, 'emptyNotebook'); + } else { + DOM.removeClass(this.topCellToolbar, 'emptyNotebook'); + } } private getCellToolbarActions(menu: IMenu, alwaysFillSecondaryActions: boolean): { primary: IAction[], secondary: IAction[] } { From 728c1eda83203b3f6980386e0839a9f6609e9a69 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 13 Aug 2020 07:55:19 -0700 Subject: [PATCH 208/736] avoid top toolbar overflow --- src/vs/workbench/contrib/notebook/browser/media/notebook.css | 4 ++++ .../contrib/notebook/browser/notebookEditorWidget.ts | 1 + src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts | 2 ++ 3 files changed, 7 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 4c05e20b374..171f0e5f8c6 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -26,6 +26,10 @@ z-index: 100; } +.monaco-workbench .notebookOverlay .cell-list-container { + overflow: hidden; +} + .monaco-workbench .notebookOverlay .cell-list-container .overflowingContentWidgets > div { z-index: 600 !important; /* @rebornix: larger than the editor title bar */ diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 5350c33cfb5..82d7940a114 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1678,6 +1678,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._localStore.clear(); this._list?.dispose(); + this._listTopCellToolbar?.dispose(); this._overlayContainer.remove(); this.viewModel?.dispose(); diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index 71fcf82ed16..7f43e90969f 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -33,6 +33,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; +import { ScrollEvent } from 'vs/base/common/scrollable'; export class TestCell extends NotebookCellTextModel { constructor( @@ -69,6 +70,7 @@ export class TestNotebookEditor implements INotebookEditor { multipleKernelsAvailable: boolean = false; onDidChangeAvailableKernels: Event = new Emitter().event; onDidChangeActiveCell: Event = new Emitter().event; + onDidScroll = new Emitter().event; uri?: URI | undefined; textModel?: NotebookTextModel | undefined; From 4782dbf16dfa1e5b451bb4388cc656d0d238f071 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 13 Aug 2020 17:56:35 +0200 Subject: [PATCH 209/736] debug contribution: move registration such that some things are not lazy --- .../contrib/debug/browser/debug.contribution.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 4db8bcb67a3..ea48d27f83c 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -60,12 +60,17 @@ debugAdapterRegisteredEmitter.event(() => { // Register these contributions lazily only once a debug adapter extension has been registered registerWorkbenchContributions(); registerColors(); - registerCommands(); registerCommandsAndActions(); registerDebugMenu(); - registerDebugPanel(); registerEditorActions(); }); +registerCommands(); +registerDebugPanel(); +const debugCategory = nls.localize('debugCategory', "Debug"); +const runCategroy = nls.localize('runCategory', "Run"); +registry.registerWorkbenchAction(SyncActionDescriptor.from(StartAction, { primary: KeyCode.F5 }, CONTEXT_IN_DEBUG_MODE.toNegated()), 'Debug: Start Debugging', debugCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.from(RunAction, { primary: KeyMod.CtrlCmd | KeyCode.F5, mac: { primary: KeyMod.WinCtrl | KeyCode.F5 } }), 'Run: Start Without Debugging', runCategroy); + registerSingleton(IDebugService, DebugService, true); registerDebugView(); registerConfiguration(); @@ -101,14 +106,9 @@ function regsiterEditorContributions(): void { function registerCommandsAndActions(): void { - const debugCategory = nls.localize('debugCategory', "Debug"); - const runCategroy = nls.localize('runCategory', "Run"); - - registry.registerWorkbenchAction(SyncActionDescriptor.from(StartAction, { primary: KeyCode.F5 }, CONTEXT_IN_DEBUG_MODE.toNegated()), 'Debug: Start Debugging', debugCategory); registry.registerWorkbenchAction(SyncActionDescriptor.from(ConfigureAction), 'Debug: Open launch.json', debugCategory); registry.registerWorkbenchAction(SyncActionDescriptor.from(AddFunctionBreakpointAction), 'Debug: Add Function Breakpoint', debugCategory); registry.registerWorkbenchAction(SyncActionDescriptor.from(ReapplyBreakpointsAction), 'Debug: Reapply All Breakpoints', debugCategory); - registry.registerWorkbenchAction(SyncActionDescriptor.from(RunAction, { primary: KeyMod.CtrlCmd | KeyCode.F5, mac: { primary: KeyMod.WinCtrl | KeyCode.F5 } }), 'Run: Start Without Debugging', runCategroy); registry.registerWorkbenchAction(SyncActionDescriptor.from(RemoveAllBreakpointsAction), 'Debug: Remove All Breakpoints', debugCategory); registry.registerWorkbenchAction(SyncActionDescriptor.from(EnableAllBreakpointsAction), 'Debug: Enable All Breakpoints', debugCategory); registry.registerWorkbenchAction(SyncActionDescriptor.from(DisableAllBreakpointsAction), 'Debug: Disable All Breakpoints', debugCategory); From f4b3ea059d56d98a1cca107e9e085b704c3a6ed5 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 13 Aug 2020 18:04:55 +0200 Subject: [PATCH 210/736] debug: add to watch should open the watch view, not debug viewlet #103414 --- .../workbench/contrib/debug/browser/debugEditorActions.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index 184cf2c5912..3d699d77e69 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -9,8 +9,7 @@ import { Range } from 'vs/editor/common/core/range'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ServicesAccessor, registerEditorAction, EditorAction, IActionOptions } from 'vs/editor/browser/editorExtensions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, VIEWLET_ID, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, REPL_VIEW_ID, CONTEXT_STEP_INTO_TARGETS_SUPPORTED } from 'vs/workbench/contrib/debug/common/debug'; -import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, REPL_VIEW_ID, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, WATCH_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { openBreakpointSource } from 'vs/workbench/contrib/debug/browser/breakpointsView'; @@ -210,13 +209,13 @@ class SelectionToWatchExpressionsAction extends EditorAction { async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { const debugService = accessor.get(IDebugService); - const viewletService = accessor.get(IViewletService); + const viewsService = accessor.get(IViewsService); if (!editor.hasModel()) { return; } const text = editor.getModel().getValueInRange(editor.getSelection()); - await viewletService.openViewlet(VIEWLET_ID); + await viewsService.openView(WATCH_VIEW_ID); debugService.addWatchExpression(text); } } From b557a371b29618a074237950d36fb352e26638cf Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 13 Aug 2020 18:21:06 +0200 Subject: [PATCH 211/736] debug: display badge on view container where the CALL STACK is #103414 --- src/vs/workbench/contrib/debug/browser/debugService.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 1cadf8fc979..fa3df30dc4d 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -32,7 +32,7 @@ import { IAction, Action } from 'vs/base/common/actions'; import { deepClone, equals } from 'vs/base/common/objects'; import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession'; 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, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IStackFrame, getStateLabel, IDebugSessionOptions, CONTEXT_DEBUG_UX, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, IGlobalConfig } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IStackFrame, getStateLabel, IDebugSessionOptions, CONTEXT_DEBUG_UX, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, IGlobalConfig, CALLSTACK_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug'; import { getExtensionHostDebugSession } from 'vs/workbench/contrib/debug/common/debugUtils'; import { isErrorWithActions } from 'vs/base/common/errorsWithActions'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -41,7 +41,7 @@ import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { TaskRunResult, DebugTaskRunner } from 'vs/workbench/contrib/debug/browser/debugTaskRunner'; import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; -import { IViewsService } from 'vs/workbench/common/views'; +import { IViewsService, IViewDescriptorService } from 'vs/workbench/common/views'; import { generateUuid } from 'vs/base/common/uuid'; import { DebugStorage } from 'vs/workbench/contrib/debug/common/debugStorage'; import { DebugTelemetry } from 'vs/workbench/contrib/debug/common/debugTelemetry'; @@ -77,6 +77,7 @@ export class DebugService implements IDebugService { @IEditorService private readonly editorService: IEditorService, @IViewletService private readonly viewletService: IViewletService, @IViewsService private readonly viewsService: IViewsService, + @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, @INotificationService private readonly notificationService: INotificationService, @IDialogService private readonly dialogService: IDialogService, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @@ -166,7 +167,10 @@ export class DebugService implements IDebugService { this.activity.dispose(); } if (numberOfSessions > 0) { - this.activity = this.activityService.showViewContainerActivity(VIEWLET_ID, { badge: new NumberBadge(numberOfSessions, n => n === 1 ? nls.localize('1activeSession', "1 active session") : nls.localize('nActiveSessions', "{0} active sessions", n)) }); + const viewContainer = this.viewDescriptorService.getViewContainerByViewId(CALLSTACK_VIEW_ID); + if (viewContainer) { + this.activity = this.activityService.showViewContainerActivity(viewContainer.id, { badge: new NumberBadge(numberOfSessions, n => n === 1 ? nls.localize('1activeSession', "1 active session") : nls.localize('nActiveSessions', "{0} active sessions", n)) }); + } } })); this.toDispose.push(this.model.onDidChangeBreakpoints(() => setBreakpointsExistContext())); From da1b424820326986f2a53f8e89fe01ba67e62da0 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 13 Aug 2020 18:25:18 +0200 Subject: [PATCH 212/736] fixes #104570 --- src/vs/workbench/contrib/debug/browser/callStackView.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 1153b13d0a3..e863af1ccaa 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -553,12 +553,7 @@ class SessionsRenderer implements ICompressibleTreeRenderer s.parentSession === session); - if (!hasChildSessions) { - data.stateLabel.textContent = nls.localize({ key: 'running', comment: ['indicates state'] }, "Running"); - } else { - data.stateLabel.style.display = 'none'; - } + data.stateLabel.textContent = nls.localize({ key: 'running', comment: ['indicates state'] }, "Running"); } } From cd2a827f63579579db1e478aebb4e9efb71f38e2 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 13 Aug 2020 18:42:38 +0200 Subject: [PATCH 213/736] breakpointEditorContribution: do not listen before a debug adapter is registered --- .../contrib/debug/browser/breakpointEditorContribution.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts index c2bbe5e9a35..41b877f6723 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts @@ -35,6 +35,7 @@ import { isSafari } from 'vs/base/browser/browser'; import { registerThemingParticipant, themeColorFromId } from 'vs/platform/theme/common/themeService'; import { registerColor } from 'vs/platform/theme/common/colorRegistry'; import { ILabelService } from 'vs/platform/label/common/label'; +import { debugAdapterRegisteredEmitter } from 'vs/workbench/contrib/debug/browser/debugConfigurationManager'; const $ = dom.$; @@ -167,8 +168,11 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi @ILabelService private readonly labelService: ILabelService ) { this.breakpointWidgetVisible = CONTEXT_BREAKPOINT_WIDGET_VISIBLE.bindTo(contextKeyService); - this.registerListeners(); this.setDecorationsScheduler = new RunOnceScheduler(() => this.setDecorations(), 30); + debugAdapterRegisteredEmitter.event(() => { + this.registerListeners(); + this.setDecorationsScheduler.schedule(); + }); } private registerListeners(): void { From bea0de31e4ac83d7146c64115af3e57e2a5546d8 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 13 Aug 2020 18:51:07 +0200 Subject: [PATCH 214/736] debug: register editor actions also not lazy --- src/vs/workbench/contrib/debug/browser/callStackView.ts | 3 +-- src/vs/workbench/contrib/debug/browser/debug.contribution.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index e863af1ccaa..23009759915 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -488,8 +488,7 @@ class SessionsRenderer implements ICompressibleTreeRenderer { registerColors(); registerCommandsAndActions(); registerDebugMenu(); - registerEditorActions(); }); +registerEditorActions(); registerCommands(); registerDebugPanel(); const debugCategory = nls.localize('debugCategory', "Debug"); From 1a4999bb03c4856226c03c13f5ed9ff70d8d5a74 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 13 Aug 2020 09:57:18 -0700 Subject: [PATCH 215/736] Remove @optional, convert native service to contribution Fixes #90098 --- .../contrib/terminal/browser/terminal.ts | 8 ++- .../terminal/browser/terminalConfigHelper.ts | 6 ++- .../terminal/browser/terminalService.ts | 52 ++++++------------- .../contrib/terminal/common/terminal.ts | 22 ++++---- .../electron-browser/terminal.contribution.ts | 9 ++-- ...rvice.ts => terminalNativeContribution.ts} | 48 ++++++++++------- .../test/browser/terminalConfigHelper.test.ts | 40 +++++++------- 7 files changed, 95 insertions(+), 90 deletions(-) rename src/vs/workbench/contrib/terminal/electron-browser/{terminalNativeService.ts => terminalNativeContribution.ts} (71%) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 81c21a43511..b6e1cd214f2 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -7,7 +7,7 @@ import { Terminal as XTermTerminal } from 'xterm'; import { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; import { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11'; import { WebglAddon as XTermWebglAddon } from 'xterm-addon-webgl'; -import { IWindowsShellHelper, ITerminalConfigHelper, ITerminalChildProcess, IShellLaunchConfig, IDefaultShellAndArgsRequest, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, ITerminalProcessExtHostProxy, ICommandTracker, INavigationMode, TitleEventSource, ITerminalDimensions, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IWindowsShellHelper, ITerminalConfigHelper, ITerminalChildProcess, IShellLaunchConfig, IDefaultShellAndArgsRequest, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, ITerminalProcessExtHostProxy, ICommandTracker, INavigationMode, TitleEventSource, ITerminalDimensions, ITerminalLaunchError, ITerminalNativeWindowsDelegate, LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProcessEnvironment, Platform } from 'vs/base/common/platform'; import { Event } from 'vs/base/common/event'; @@ -149,6 +149,12 @@ export interface ITerminalService { setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; manageWorkspaceShellPermissions(): void; + /** + * Injects native Windows functionality into the service. + */ + setNativeWindowsDelegate(delegate: ITerminalNativeWindowsDelegate): void; + setLinuxDistro(linuxDistro: LinuxDistro): void; + /** * Takes a path and returns the properly escaped path to send to the terminal. * On Windows, this included trying to prepare the path for WSL if needed. diff --git a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts index 82f060e64cc..e39451c4b69 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts @@ -35,13 +35,13 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { private _charMeasureElement: HTMLElement | undefined; private _lastFontMeasurement: ITerminalFont | undefined; + private _linuxDistro: LinuxDistro = LinuxDistro.Unknown; public config!: ITerminalConfiguration; private readonly _onWorkspacePermissionsChanged = new Emitter(); public get onWorkspacePermissionsChanged(): Event { return this._onWorkspacePermissionsChanged.event; } public constructor( - private readonly _linuxDistro: LinuxDistro, @IConfigurationService private readonly _configurationService: IConfigurationService, @IExtensionManagementService private readonly _extensionManagementService: IExtensionManagementService, @INotificationService private readonly _notificationService: INotificationService, @@ -62,6 +62,10 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { storageKeysSyncRegistryService.registerStorageKey({ key: 'terminalConfigHelper/launchRecommendationsIgnore', version: 1 }); } + public setLinuxDistro(linuxDistro: LinuxDistro) { + this._linuxDistro = linuxDistro; + } + private _updateConfig(): void { this.config = this._configurationService.getValue(TERMINAL_CONFIG_SECTION); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 2fcce1787c2..15154160cbd 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { TERMINAL_VIEW_ID, IShellLaunchConfig, ITerminalConfigHelper, ITerminalNativeService, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, ITerminalProcessExtHostProxy, IShellDefinition, LinuxDistro, KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TERMINAL_VIEW_ID, IShellLaunchConfig, ITerminalConfigHelper, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, ITerminalProcessExtHostProxy, IShellDefinition, LinuxDistro, KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE, ITerminalLaunchError, ITerminalNativeWindowsDelegate } from 'vs/workbench/contrib/terminal/common/terminal'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { TerminalViewPane } from 'vs/workbench/contrib/terminal/browser/terminalView'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab'; -import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; import { ITerminalService, ITerminalInstance, ITerminalTab, TerminalShellType, WindowsShellType, ITerminalExternalLinkProvider } from 'vs/workbench/contrib/terminal/browser/terminal'; @@ -59,6 +59,7 @@ export class TerminalService implements ITerminalService { private _configHelper: TerminalConfigHelper; private _terminalContainer: HTMLElement | undefined; + private _nativeWindowsDelegate: ITerminalNativeWindowsDelegate | undefined; public get configHelper(): ITerminalConfigHelper { return this._configHelper; } @@ -91,8 +92,6 @@ export class TerminalService implements ITerminalService { private readonly _onRequestAvailableShells = new Emitter(); public get onRequestAvailableShells(): Event { return this._onRequestAvailableShells.event; } - private readonly _terminalNativeService: ITerminalNativeService | undefined; - constructor( @IContextKeyService private _contextKeyService: IContextKeyService, @IWorkbenchLayoutService private _layoutService: IWorkbenchLayoutService, @@ -104,34 +103,17 @@ export class TerminalService implements ITerminalService { @IQuickInputService private _quickInputService: IQuickInputService, @IConfigurationService private _configurationService: IConfigurationService, @IViewsService private _viewsService: IViewsService, - @IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService, - // HACK: Ideally TerminalNativeService would depend on TerminalService and inject the - // additional native functionality into it. - @optional(ITerminalNativeService) terminalNativeService: ITerminalNativeService + @IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService ) { - // @optional could give undefined and properly typing it breaks service registration - this._terminalNativeService = terminalNativeService as ITerminalNativeService | undefined; - this._activeTabIndex = 0; this._isShuttingDown = false; this._findState = new FindReplaceState(); lifecycleService.onBeforeShutdown(async event => event.veto(this._onBeforeShutdown())); lifecycleService.onShutdown(() => this._onShutdown()); - if (this._terminalNativeService) { - this._terminalNativeService.onRequestFocusActiveInstance(() => { - if (this.terminalInstances.length > 0) { - const terminal = this.getActiveInstance(); - if (terminal) { - terminal.focus(); - } - } - }); - this._terminalNativeService.onOsResume(() => this._onOsResume()); - } this._terminalFocusContextKey = KEYBINDING_CONTEXT_TERMINAL_FOCUS.bindTo(this._contextKeyService); this._terminalShellTypeContextKey = KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE.bindTo(this._contextKeyService); this._findWidgetVisible = KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE.bindTo(this._contextKeyService); - this._configHelper = this._instantiationService.createInstance(TerminalConfigHelper, this._terminalNativeService?.linuxDistro || LinuxDistro.Unknown); + this._configHelper = this._instantiationService.createInstance(TerminalConfigHelper); this.onTabDisposed(tab => this._removeTab(tab)); this.onActiveTabChanged(() => { const instance = this.getActiveInstance(); @@ -142,6 +124,14 @@ export class TerminalService implements ITerminalService { this._handleContextKeys(); } + public setNativeWindowsDelegate(delegate: ITerminalNativeWindowsDelegate): void { + this._nativeWindowsDelegate = delegate; + } + + public setLinuxDistro(linuxDistro: LinuxDistro): void { + this._configHelper.setLinuxDistro(linuxDistro); + } + private _handleContextKeys(): void { const terminalIsOpenContext = KEYBINDING_CONTEXT_TERMINAL_IS_OPEN.bindTo(this._contextKeyService); @@ -225,14 +215,6 @@ export class TerminalService implements ITerminalService { this.terminalInstances.forEach(instance => instance.dispose(true)); } - private _onOsResume(): void { - const activeTab = this.getActiveTab(); - if (!activeTab) { - return; - } - activeTab.terminalInstances.forEach(instance => instance.forceRedraw()); - } - public getTabLabels(): string[] { return this._terminalTabs.filter(tab => tab.terminalInstances.length > 0).map((tab, index) => `${index + 1}: ${tab.title ? tab.title : ''}`); } @@ -543,8 +525,8 @@ export class TerminalService implements ITerminalService { return; } else if (shellType === WindowsShellType.Wsl) { - if (this._terminalNativeService && this._terminalNativeService.getWindowsBuildNumber() >= 17063) { - c(this._terminalNativeService.getWslPath(originalPath)); + if (this._nativeWindowsDelegate && this._nativeWindowsDelegate.getWindowsBuildNumber() >= 17063) { + c(this._nativeWindowsDelegate.getWslPath(originalPath)); } else { c(originalPath.replace(/\\/g, '/')); } @@ -558,9 +540,9 @@ export class TerminalService implements ITerminalService { } } else { const lowerExecutable = executable.toLowerCase(); - if (this._terminalNativeService && this._terminalNativeService.getWindowsBuildNumber() >= 17063 && + if (this._nativeWindowsDelegate && this._nativeWindowsDelegate.getWindowsBuildNumber() >= 17063 && (lowerExecutable.indexOf('wsl') !== -1 || (lowerExecutable.indexOf('bash.exe') !== -1 && lowerExecutable.toLowerCase().indexOf('git') === -1))) { - c(this._terminalNativeService.getWslPath(originalPath)); + c(this._nativeWindowsDelegate.getWslPath(originalPath)); return; } else if (hasSpace) { c('"' + originalPath + '"'); diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index e0d0fb53029..5edbd197dbd 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -7,7 +7,6 @@ import * as nls from 'vs/nls'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { URI } from 'vs/base/common/uri'; import { OperatingSystem } from 'vs/base/common/platform'; import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable'; @@ -55,8 +54,6 @@ export const NEVER_MEASURE_RENDER_TIME_STORAGE_KEY = 'terminal.integrated.neverM // trying to create the corressponding object on the ext host. export const EXT_HOST_CREATION_DELAY = 100; -export const ITerminalNativeService = createDecorator('terminalNativeService'); - export const TerminalCursorStyle = { BLOCK: 'block', LINE: 'line', @@ -230,18 +227,17 @@ export interface IShellLaunchConfig { } /** - * Provides access to native or electron APIs to other terminal services. + * Provides access to native Windows calls that can be injected into non-native layers. */ -export interface ITerminalNativeService { - readonly _serviceBrand: undefined; - - readonly linuxDistro: LinuxDistro; - - readonly onRequestFocusActiveInstance: Event; - readonly onOsResume: Event; - +export interface ITerminalNativeWindowsDelegate { + /** + * Gets the Windows build number, eg. this would be `19041` for Windows 10 version 2004 + */ getWindowsBuildNumber(): number; - whenFileDeleted(path: URI): Promise; + /** + * Converts a regular Windows path into the WSL path equivalent, eg. `C:\` -> `/mnt/c` + * @param path The Windows path. + */ getWslPath(path: string): Promise; } diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts index 1721bf226a6..263c15cb666 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts @@ -3,22 +3,25 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalInstanceService } from 'vs/workbench/contrib/terminal/electron-browser/terminalInstanceService'; import { getSystemShell } from 'vs/workbench/contrib/terminal/node/terminal'; -import { TerminalNativeService } from 'vs/workbench/contrib/terminal/electron-browser/terminalNativeService'; -import { ITerminalNativeService } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TerminalNativeContribution } from 'vs/workbench/contrib/terminal/electron-browser/terminalNativeContribution'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; import { getTerminalShellConfiguration } from 'vs/workbench/contrib/terminal/common/terminalConfiguration'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; // This file contains additional desktop-only contributions on top of those in browser/ // Register services -registerSingleton(ITerminalNativeService, TerminalNativeService, true); registerSingleton(ITerminalInstanceService, TerminalInstanceService, true); +const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); +workbenchRegistry.registerWorkbenchContribution(TerminalNativeContribution, LifecyclePhase.Ready); + // Register configurations const configurationRegistry = Registry.as(Extensions.Configuration); configurationRegistry.registerConfiguration(getTerminalShellConfiguration(getSystemShell)); diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeContribution.ts similarity index 71% rename from src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts rename to src/vs/workbench/contrib/terminal/electron-browser/terminalNativeContribution.ts index 84030ff44a7..52d755f78b2 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeContribution.ts @@ -5,40 +5,40 @@ import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { IOpenFileRequest } from 'vs/platform/windows/common/windows'; -import { ITerminalNativeService, LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; import { URI } from 'vs/base/common/uri'; import { IFileService } from 'vs/platform/files/common/files'; import { getWindowsBuildNumber, linuxDistro } from 'vs/workbench/contrib/terminal/node/terminal'; import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { execFile } from 'child_process'; -import { Emitter, Event } from 'vs/base/common/event'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { registerRemoteContributions } from 'vs/workbench/contrib/terminal/electron-browser/terminalRemote'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { Disposable } from 'vs/base/common/lifecycle'; import { INativeOpenFileRequest } from 'vs/platform/windows/node/window'; +import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -export class TerminalNativeService extends Disposable implements ITerminalNativeService { +export class TerminalNativeContribution extends Disposable implements IWorkbenchContribution { public _serviceBrand: undefined; - public get linuxDistro(): LinuxDistro { return linuxDistro; } - - private readonly _onRequestFocusActiveInstance = this._register(new Emitter()); - public get onRequestFocusActiveInstance(): Event { return this._onRequestFocusActiveInstance.event; } - private readonly _onOsResume = this._register(new Emitter()); - public get onOsResume(): Event { return this._onOsResume.event; } - constructor( @IFileService private readonly _fileService: IFileService, + @ITerminalService private readonly _terminalService: ITerminalService, @IInstantiationService readonly instantiationService: IInstantiationService, - @IRemoteAgentService remoteAgentService: IRemoteAgentService, - @IElectronService electronService: IElectronService + @IRemoteAgentService readonly remoteAgentService: IRemoteAgentService, + @IElectronService readonly electronService: IElectronService ) { super(); - ipcRenderer.on('vscode:openFiles', (event: unknown, request: IOpenFileRequest) => this._onOpenFileRequest(request)); - this._register(electronService.onOSResume(() => this._onOsResume.fire())); + ipcRenderer.on('vscode:openFiles', (_: unknown, request: IOpenFileRequest) => this._onOpenFileRequest(request)); + this._register(electronService.onOSResume(() => this._onOsResume())); + + this._terminalService.setLinuxDistro(linuxDistro); + this._terminalService.setNativeWindowsDelegate({ + getWslPath: this._getWslPath.bind(this), + getWindowsBuildNumber: this._getWindowsBuildNumber.bind(this) + }); const connection = remoteAgentService.getConnection(); if (connection && connection.remoteAuthority) { @@ -46,18 +46,28 @@ export class TerminalNativeService extends Disposable implements ITerminalNative } } + private _onOsResume(): void { + const activeTab = this._terminalService.getActiveTab(); + if (!activeTab) { + return; + } + activeTab.terminalInstances.forEach(instance => instance.forceRedraw()); + } + private async _onOpenFileRequest(request: INativeOpenFileRequest): Promise { // if the request to open files is coming in from the integrated terminal (identified though // the termProgram variable) and we are instructed to wait for editors close, wait for the // marker file to get deleted and then focus back to the integrated terminal. if (request.termProgram === 'vscode' && request.filesToWait) { const waitMarkerFileUri = URI.revive(request.filesToWait.waitMarkerFileUri); - await this.whenFileDeleted(waitMarkerFileUri); - this._onRequestFocusActiveInstance.fire(); + await this._whenFileDeleted(waitMarkerFileUri); + + // Focus active terminal + this._terminalService.getActiveInstance()?.focus(); } } - public whenFileDeleted(path: URI): Promise { + private _whenFileDeleted(path: URI): Promise { // Complete when wait marker file is deleted return new Promise(resolve => { let running = false; @@ -80,7 +90,7 @@ export class TerminalNativeService extends Disposable implements ITerminalNative * Converts a path to a path on WSL using the wslpath utility. * @param path The original path. */ - public getWslPath(path: string): Promise { + private _getWslPath(path: string): Promise { if (getWindowsBuildNumber() < 17063) { throw new Error('wslpath does not exist on Windows build < 17063'); } @@ -92,7 +102,7 @@ export class TerminalNativeService extends Disposable implements ITerminalNative }); } - public getWindowsBuildNumber(): number { + private _getWindowsBuildNumber(): number { return getWindowsBuildNumber(); } } diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalConfigHelper.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalConfigHelper.test.ts index fad3e60d818..eed4382af0a 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalConfigHelper.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalConfigHelper.test.ts @@ -21,7 +21,7 @@ suite('Workbench - TerminalConfigHelper', () => { // const configurationService = new TestConfigurationService(); // configurationService.setUserConfiguration('editor', { fontFamily: 'foo' }); // configurationService.setUserConfiguration('terminal', { integrated: { fontFamily: 'bar' } }); - // const configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); + // const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); // configHelper.panelContainer = fixture; // assert.equal(configHelper.getFont().fontFamily, 'bar', 'terminal.integrated.fontFamily should be selected over editor.fontFamily'); // }); @@ -30,7 +30,8 @@ suite('Workbench - TerminalConfigHelper', () => { const configurationService = new TestConfigurationService(); configurationService.setUserConfiguration('editor', { fontFamily: 'foo' }); configurationService.setUserConfiguration('terminal', { integrated: { fontFamily: null } }); - const configHelper = new TerminalConfigHelper(LinuxDistro.Fedora, configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + configHelper.setLinuxDistro(LinuxDistro.Fedora); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontFamily, '\'DejaVu Sans Mono\', monospace', 'Fedora should have its font overridden when terminal.integrated.fontFamily not set'); }); @@ -39,7 +40,8 @@ suite('Workbench - TerminalConfigHelper', () => { const configurationService = new TestConfigurationService(); configurationService.setUserConfiguration('editor', { fontFamily: 'foo' }); configurationService.setUserConfiguration('terminal', { integrated: { fontFamily: null } }); - const configHelper = new TerminalConfigHelper(LinuxDistro.Ubuntu, configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + configHelper.setLinuxDistro(LinuxDistro.Ubuntu); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontFamily, '\'Ubuntu Mono\', monospace', 'Ubuntu should have its font overridden when terminal.integrated.fontFamily not set'); }); @@ -48,7 +50,7 @@ suite('Workbench - TerminalConfigHelper', () => { const configurationService = new TestConfigurationService(); configurationService.setUserConfiguration('editor', { fontFamily: 'foo' }); configurationService.setUserConfiguration('terminal', { integrated: { fontFamily: null } }); - const configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontFamily, 'foo', 'editor.fontFamily should be the fallback when terminal.integrated.fontFamily not set'); }); @@ -66,7 +68,7 @@ suite('Workbench - TerminalConfigHelper', () => { fontSize: 10 } }); - let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontSize, 10, 'terminal.integrated.fontSize should be selected over editor.fontSize'); @@ -79,11 +81,12 @@ suite('Workbench - TerminalConfigHelper', () => { fontSize: 0 } }); - configHelper = new TerminalConfigHelper(LinuxDistro.Ubuntu, configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + configHelper.setLinuxDistro(LinuxDistro.Ubuntu); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontSize, 8, 'The minimum terminal font size (with adjustment) should be used when terminal.integrated.fontSize less than it'); - configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontSize, 6, 'The minimum terminal font size should be used when terminal.integrated.fontSize less than it'); @@ -96,7 +99,7 @@ suite('Workbench - TerminalConfigHelper', () => { fontSize: 1500 } }); - configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontSize, 25, 'The maximum terminal font size should be used when terminal.integrated.fontSize more than it'); @@ -109,11 +112,12 @@ suite('Workbench - TerminalConfigHelper', () => { fontSize: null } }); - configHelper = new TerminalConfigHelper(LinuxDistro.Ubuntu, configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + configHelper.setLinuxDistro(LinuxDistro.Ubuntu); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontSize, EDITOR_FONT_DEFAULTS.fontSize + 2, 'The default editor font size (with adjustment) should be used when terminal.integrated.fontSize is not set'); - configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontSize, EDITOR_FONT_DEFAULTS.fontSize, 'The default editor font size should be used when terminal.integrated.fontSize is not set'); }); @@ -131,7 +135,7 @@ suite('Workbench - TerminalConfigHelper', () => { lineHeight: 2 } }); - let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().lineHeight, 2, 'terminal.integrated.lineHeight should be selected over editor.lineHeight'); @@ -145,7 +149,7 @@ suite('Workbench - TerminalConfigHelper', () => { lineHeight: 0 } }); - configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().lineHeight, 1, 'editor.lineHeight should be 1 when terminal.integrated.lineHeight not set'); }); @@ -158,7 +162,7 @@ suite('Workbench - TerminalConfigHelper', () => { } }); - let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), true, 'monospace is monospaced'); }); @@ -170,7 +174,7 @@ suite('Workbench - TerminalConfigHelper', () => { fontFamily: 'sans-serif' } }); - let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), false, 'sans-serif is not monospaced'); }); @@ -182,7 +186,7 @@ suite('Workbench - TerminalConfigHelper', () => { fontFamily: 'serif' } }); - let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), false, 'serif is not monospaced'); }); @@ -198,7 +202,7 @@ suite('Workbench - TerminalConfigHelper', () => { } }); - let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), true, 'monospace is monospaced'); }); @@ -214,7 +218,7 @@ suite('Workbench - TerminalConfigHelper', () => { } }); - let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), false, 'sans-serif is not monospaced'); }); @@ -230,7 +234,7 @@ suite('Workbench - TerminalConfigHelper', () => { } }); - let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), false, 'serif is not monospaced'); }); From f542cd1f76a1ec5878eb9f68978ad56dae6ac1b6 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 13 Aug 2020 11:00:40 -0700 Subject: [PATCH 216/736] Queue initial term data events for ext host delayed init Fixes #103697 --- .../api/browser/mainThreadTerminalService.ts | 4 +++ .../contrib/terminal/browser/terminal.ts | 8 ++++++ .../terminal/browser/terminalInstance.ts | 25 ++++++++++++++++--- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index d518ec92f6e..b64f47fad6d 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -154,6 +154,10 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._dataEventTracker = this._instantiationService.createInstance(TerminalDataEventTracker, (id, data) => { this._onTerminalData(id, data); }); + // Send initial events if they exist + this._terminalService.terminalInstances.forEach(t => { + t.initialDataEvents?.forEach(d => this._onTerminalData(t.id, d)); + }); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index b6e1cd214f2..a1f25f55fb9 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -287,6 +287,14 @@ export interface ITerminalInstance { readonly areLinksReady: boolean; + /** + * Returns an array of data events that have fired within the first 10 seconds. If this is + * called 10 seconds after the terminal has existed the result will be undefined. This is useful + * when objects that depend on the data events have delayed initialization, like extension + * hosts. + */ + readonly initialDataEvents: string[] | undefined; + /** A promise that resolves when the terminal's pty/process have been created. */ processReady: Promise; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index ee16eb81e97..6f864a208f5 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -98,6 +98,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { private _titleReadyPromise: Promise; private _titleReadyComplete: ((title: string) => any) | undefined; private _areLinksReady: boolean = false; + private _initialDataEvents: string[] | undefined = []; private _messageTitleDisposable: IDisposable | undefined; @@ -131,6 +132,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // TODO: Should this be an event as it can fire twice? public get processReady(): Promise { return this._processManager.ptyProcessReady; } public get areLinksReady(): boolean { return this._areLinksReady; } + public get initialDataEvents(): string[] | undefined { return this._initialDataEvents; } public get exitCode(): number | undefined { return this._exitCode; } public get title(): string { return this._title; } public get hadFocusOnExit(): boolean { return this._hadFocusOnExit; } @@ -231,6 +233,20 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this.updateAccessibilitySupport(); } })); + + // Clear out initial data events after 10 seconds, hopefully extension hosts are up and + // running at that point. + let initialDataEventsTimeout: NodeJS.Timeout | undefined = setTimeout(() => { + initialDataEventsTimeout = undefined; + this._initialDataEvents = undefined; + }, 10000); + this._register({ + dispose: () => { + if (initialDataEventsTimeout) { + clearTimeout(initialDataEventsTimeout); + } + } + }); } public addDisposable(disposable: IDisposable): void { @@ -862,11 +878,12 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { protected _createProcessManager(): void { this._processManager = this._instantiationService.createInstance(TerminalProcessManager, this._id, this._configHelper); - this._processManager.onProcessReady(() => { - this._onProcessIdReady.fire(this); - }); + this._processManager.onProcessReady(() => this._onProcessIdReady.fire(this)); this._processManager.onProcessExit(exitCode => this._onProcessExit(exitCode)); - this._processManager.onProcessData(data => this._onData.fire(data)); + this._processManager.onProcessData(data => { + this._initialDataEvents?.push(data); + this._onData.fire(data); + }); this._processManager.onProcessOverrideDimensions(e => this.setDimensions(e)); this._processManager.onProcessResolvedShellLaunchConfig(e => this._setResolvedShellLaunchConfig(e)); this._processManager.onEnvironmentVariableInfoChanged(e => this._onEnvironmentVariableInfoChanged(e)); From ea28750f245d412dbd7b8c9fe0309abf9a109550 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 13 Aug 2020 11:10:55 -0700 Subject: [PATCH 217/736] fix #104569. --- .../notebook/browser/notebookServiceImpl.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 99c441658f7..acc43755f64 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -361,6 +361,11 @@ export class NotebookService extends Disposable implements INotebookService, ICu if (CopyAction) { this._register(CopyAction.addImplementation(PRIORITY, accessor => { + const activeElement = document.activeElement; + if (activeElement && ['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) >= 0) { + return false; + } + const { editor, activeCell } = getContext(); if (!editor || !activeCell) { return false; @@ -382,6 +387,11 @@ export class NotebookService extends Disposable implements INotebookService, ICu if (PasteAction) { PasteAction.addImplementation(PRIORITY, () => { + const activeElement = document.activeElement; + if (activeElement && ['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) >= 0) { + return false; + } + const pasteCells = this.getToCopy(); if (!pasteCells) { @@ -465,6 +475,11 @@ export class NotebookService extends Disposable implements INotebookService, ICu if (CutAction) { CutAction.addImplementation(PRIORITY, accessor => { + const activeElement = document.activeElement; + if (activeElement && ['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) >= 0) { + return false; + } + const { editor, activeCell } = getContext(); if (!editor || !activeCell) { return false; From 9823308dfa5f170a1e1c6205d33bbe3f08968dd7 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 13 Aug 2020 11:38:31 -0700 Subject: [PATCH 218/736] Fix output selector going under cell drag handle Fix #104579 --- src/vs/workbench/contrib/notebook/browser/media/notebook.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 171f0e5f8c6..052f2c8b634 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -140,6 +140,7 @@ transform: translate3d(0px, 0px, 0px); cursor: auto; box-sizing: border-box; + z-index: 27; /* Over drag handle */ } .monaco-workbench .notebookOverlay .output p { @@ -179,7 +180,6 @@ height: 16px; cursor: pointer; padding: 4px; - z-index: 27; } .monaco-workbench .notebookOverlay .output .error_message { @@ -238,6 +238,7 @@ position: relative; left: -23px; cursor: pointer; + z-index: 27; /* Over drag handle */ } .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.collapsed .notebook-folding-indicator, From cec2927609923421f612d20475266e8c0e32d9b6 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 13 Aug 2020 11:50:41 -0700 Subject: [PATCH 219/736] Remove node usage from browser --- src/vs/workbench/contrib/terminal/browser/terminalInstance.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 6f864a208f5..01fbd5bded8 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -236,14 +236,14 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // Clear out initial data events after 10 seconds, hopefully extension hosts are up and // running at that point. - let initialDataEventsTimeout: NodeJS.Timeout | undefined = setTimeout(() => { + let initialDataEventsTimeout: number | undefined = window.setTimeout(() => { initialDataEventsTimeout = undefined; this._initialDataEvents = undefined; }, 10000); this._register({ dispose: () => { if (initialDataEventsTimeout) { - clearTimeout(initialDataEventsTimeout); + window.clearTimeout(initialDataEventsTimeout); } } }); From 061122ba104d2d4931718ede88bd061d1ab254fb Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 13 Aug 2020 11:59:47 -0700 Subject: [PATCH 220/736] fix #104581 --- .../workbench/contrib/notebook/browser/contrib/coreActions.ts | 2 +- .../contrib/notebook/browser/view/renderers/cellRenderer.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index f31e19d92bd..63f2adeb309 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -1308,7 +1308,7 @@ export class ChangeCellLanguageAction extends NotebookCellAction { const providerLanguages = [...context.notebookEditor.viewModel!.notebookDocument.languages, 'markdown']; providerLanguages.forEach(languageId => { let description: string; - if (languageId === context.cell.language) { + if (context.cell.cellKind === CellKind.Markdown ? (languageId === 'markdown') : (languageId === context.cell.language)) { description = localize('languageDescription', "({0}) - Current Language", languageId); } else { description = localize('languageDescriptionConfigured', "({0})", languageId); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 1c9559cb052..9c7bd78acf1 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -813,7 +813,7 @@ export class CellLanguageStatusBarItem extends Disposable { } private render(): void { - const modeId = this.modeService.getModeIdForLanguageName(this.cell!.language) || this.cell!.language; + const modeId = this.cell?.cellKind === CellKind.Markdown ? 'markdown' : this.modeService.getModeIdForLanguageName(this.cell!.language) || this.cell!.language; this.labelElement.textContent = this.modeService.getLanguageName(modeId) || this.modeService.getLanguageName('plaintext'); } } From 0000cfdda71eee9d546bb60e50ed28d342cde475 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Thu, 13 Aug 2020 12:11:29 -0700 Subject: [PATCH 221/736] Revert "Disable bot for joao (vacation)." This reverts commit 406315dfec3e17f6f32e4e1a5b21c59873db6b05. --- .github/classifier.json | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/classifier.json b/.github/classifier.json index 420cce5bfaf..bb313eebafb 100644 --- a/.github/classifier.json +++ b/.github/classifier.json @@ -1,7 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/microsoft/vscode-github-triage-actions/master/classifier-deep/apply/apply-labels/deep-classifier-config.schema.json", "assignees": { - "joaomoreno": {"accuracy": 1.5}, "JacksonKearl": {"accuracy": 0.5} }, "labels": { @@ -72,10 +71,10 @@ "file-watcher": {"assign": ["bpasero"]}, "font-rendering": {"assign": []}, "formatting": {"assign": []}, - "git": {"assign": ["joaomoreno"], "accuracy": 1.5}, + "git": {"assign": ["joaomoreno"]}, "gpu": {"assign": ["deepak1556"]}, "grammar": {"assign": ["mjbvz"]}, - "grid-view": {"assign": ["joaomoreno"], "accuracy": 1.5}, + "grid-view": {"assign": ["joaomoreno"]}, "html": {"assign": ["aeschli"]}, "i18n": {"assign": []}, "icon-brand": {"assign": []}, @@ -86,7 +85,7 @@ "integrated-terminal-links": {"assign": ["Tyriar"]}, "integration-test": {"assign": []}, "intellisense-config": {"assign": []}, - "ipc": {"assign": ["joaomoreno"], "accuracy": 1.5}, + "ipc": {"assign": ["joaomoreno"]}, "issue-bot": {"assign": ["chrmarti"]}, "issue-reporter": {"assign": ["RMacfarlane"]}, "javascript": {"assign": ["mjbvz"]}, @@ -98,7 +97,7 @@ "languages-diagnostics": {"assign": ["jrieken"]}, "layout": {"assign": ["sbatten"]}, "lcd-text-rendering": {"assign": []}, - "list": {"assign": ["joaomoreno"], "accuracy": 1.5}, + "list": {"assign": ["joaomoreno"]}, "log": {"assign": []}, "markdown": {"assign": ["mjbvz"]}, "marketplace": {"assign": []}, @@ -111,7 +110,7 @@ "perf-bloat": {"assign": []}, "perf-startup": {"assign": []}, "php": {"assign": ["roblourens"]}, - "portable-mode": {"assign": ["joaomoreno"], "accuracy": 1.5}, + "portable-mode": {"assign": ["joaomoreno"]}, "proxy": {"assign": []}, "quick-pick": {"assign": ["chrmarti"]}, "references-viewlet": {"assign": ["jrieken"]}, @@ -119,7 +118,7 @@ "remote": {"assign": []}, "remote-explorer": {"assign": ["alexr00"]}, "rename": {"assign": ["jrieken"]}, - "scm": {"assign": ["joaomoreno"], "accuracy": 1.5}, + "scm": {"assign": ["joaomoreno"]}, "screencast-mode": {"assign": ["lszomoru"]}, "search": {"assign": ["roblourens"]}, "search-editor": {"assign": ["JacksonKearl"]}, @@ -130,9 +129,9 @@ "simple-file-dialog": {"assign": ["alexr00"]}, "smart-select": {"assign": ["jrieken"]}, "smoke-test": {"assign": []}, - "snap": {"assign": ["joaomoreno"], "accuracy": 1.5}, + "snap": {"assign": ["joaomoreno"]}, "snippets": {"assign": ["jrieken"]}, - "splitview": {"assign": ["joaomoreno"], "accuracy": 1.5}, + "splitview": {"assign": ["joaomoreno"]}, "suggest": {"assign": ["jrieken"]}, "tasks": {"assign": ["alexr00"]}, "telemetry": {"assign": []}, @@ -141,7 +140,7 @@ "timeline-git": {"assign": ["eamodio"]}, "titlebar": {"assign": ["sbatten"]}, "tokenization": {"assign": []}, - "tree": {"assign": ["joaomoreno"], "accuracy": 1.5}, + "tree": {"assign": ["joaomoreno"]}, "typescript": {"assign": ["mjbvz"]}, "undo-redo": {"assign": []}, "unit-test": {"assign": []}, From 8d94a66de199aef753d332a1abd5c50a6a95acf0 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Thu, 13 Aug 2020 12:12:22 -0700 Subject: [PATCH 222/736] Bump actions, aadopt new "vacation" config --- .github/classifier.json | 1 + .github/workflows/author-verified.yml | 2 +- .github/workflows/commands.yml | 2 +- .github/workflows/deep-classifier-monitor.yml | 2 +- .github/workflows/deep-classifier-runner.yml | 2 +- .github/workflows/deep-classifier-scraper.yml | 2 +- .github/workflows/english-please.yml | 2 +- .github/workflows/feature-request.yml | 2 +- .github/workflows/latest-release-monitor.yml | 2 +- .github/workflows/locker.yml | 2 +- .github/workflows/needs-more-info-closer.yml | 2 +- .github/workflows/on-label.yml | 2 +- .github/workflows/on-open.yml | 2 +- .github/workflows/release-pipeline-labeler.yml | 2 +- .github/workflows/test-plan-item-validator.yml | 2 +- 15 files changed, 15 insertions(+), 14 deletions(-) diff --git a/.github/classifier.json b/.github/classifier.json index bb313eebafb..c9196fc5691 100644 --- a/.github/classifier.json +++ b/.github/classifier.json @@ -1,5 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/microsoft/vscode-github-triage-actions/master/classifier-deep/apply/apply-labels/deep-classifier-config.schema.json", + "vacation": ["joaomoreno"], "assignees": { "JacksonKearl": {"accuracy": 0.5} }, diff --git a/.github/workflows/author-verified.yml b/.github/workflows/author-verified.yml index cf67a849350..c0f5efc51d4 100644 --- a/.github/workflows/author-verified.yml +++ b/.github/workflows/author-verified.yml @@ -17,7 +17,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v32 + ref: v33 path: ./actions - name: Install Actions if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'author-verification-requested') diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml index afdf723c4ed..63e43a17703 100644 --- a/.github/workflows/commands.yml +++ b/.github/workflows/commands.yml @@ -13,7 +13,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v32 + ref: v33 - name: Install Actions run: npm install --production --prefix ./actions - name: Run Commands diff --git a/.github/workflows/deep-classifier-monitor.yml b/.github/workflows/deep-classifier-monitor.yml index 38c2c7df21d..545f74c273b 100644 --- a/.github/workflows/deep-classifier-monitor.yml +++ b/.github/workflows/deep-classifier-monitor.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v32 + ref: v33 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/deep-classifier-runner.yml b/.github/workflows/deep-classifier-runner.yml index a2f86a9da0a..3db13a01c2b 100644 --- a/.github/workflows/deep-classifier-runner.yml +++ b/.github/workflows/deep-classifier-runner.yml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v32 + ref: v33 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/deep-classifier-scraper.yml b/.github/workflows/deep-classifier-scraper.yml index 0f0de92ce0d..a78b4d14c2f 100644 --- a/.github/workflows/deep-classifier-scraper.yml +++ b/.github/workflows/deep-classifier-scraper.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v32 + ref: v33 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/english-please.yml b/.github/workflows/english-please.yml index 6728423998e..b81f12ab39f 100644 --- a/.github/workflows/english-please.yml +++ b/.github/workflows/english-please.yml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v32 + ref: v33 path: ./actions - name: Install Actions if: contains(github.event.issue.labels.*.name, '*english-please') diff --git a/.github/workflows/feature-request.yml b/.github/workflows/feature-request.yml index af3b36fa026..9d25f4a9f5c 100644 --- a/.github/workflows/feature-request.yml +++ b/.github/workflows/feature-request.yml @@ -18,7 +18,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v32 + ref: v33 - name: Install Actions if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'feature-request') run: npm install --production --prefix ./actions diff --git a/.github/workflows/latest-release-monitor.yml b/.github/workflows/latest-release-monitor.yml index 99eabe33b33..acca00a07e3 100644 --- a/.github/workflows/latest-release-monitor.yml +++ b/.github/workflows/latest-release-monitor.yml @@ -14,7 +14,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v32 + ref: v33 - name: Install Actions run: npm install --production --prefix ./actions - name: Install Storage Module diff --git a/.github/workflows/locker.yml b/.github/workflows/locker.yml index 7c2d8b119c6..776203c8c6c 100644 --- a/.github/workflows/locker.yml +++ b/.github/workflows/locker.yml @@ -14,7 +14,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v32 + ref: v33 - name: Install Actions run: npm install --production --prefix ./actions - name: Run Locker diff --git a/.github/workflows/needs-more-info-closer.yml b/.github/workflows/needs-more-info-closer.yml index fe2f76f0e62..8ecdd5fb9be 100644 --- a/.github/workflows/needs-more-info-closer.yml +++ b/.github/workflows/needs-more-info-closer.yml @@ -14,7 +14,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v32 + ref: v33 - name: Install Actions run: npm install --production --prefix ./actions - name: Run Needs More Info Closer diff --git a/.github/workflows/on-label.yml b/.github/workflows/on-label.yml index 638625f1713..732842501a6 100644 --- a/.github/workflows/on-label.yml +++ b/.github/workflows/on-label.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v32 + ref: v33 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/on-open.yml b/.github/workflows/on-open.yml index c02f5a9f117..3c003d0797a 100644 --- a/.github/workflows/on-open.yml +++ b/.github/workflows/on-open.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v32 + ref: v33 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/release-pipeline-labeler.yml b/.github/workflows/release-pipeline-labeler.yml index fdabe0ddc07..8386e8f0f9f 100644 --- a/.github/workflows/release-pipeline-labeler.yml +++ b/.github/workflows/release-pipeline-labeler.yml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v32 + ref: v33 path: ./actions - name: Checkout Repo if: github.event_name != 'issues' diff --git a/.github/workflows/test-plan-item-validator.yml b/.github/workflows/test-plan-item-validator.yml index 66beb3f1742..73da8e6b928 100644 --- a/.github/workflows/test-plan-item-validator.yml +++ b/.github/workflows/test-plan-item-validator.yml @@ -14,7 +14,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v32 + ref: v33 - name: Install Actions if: contains(github.event.issue.labels.*.name, 'testplan-item') || contains(github.event.issue.labels.*.name, 'invalid-testplan-item') run: npm install --production --prefix ./actions From 8e369907a9246ebbfb62a721a421172b7a042356 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Thu, 13 Aug 2020 16:04:50 -0400 Subject: [PATCH 223/736] Removes tabs completely (required for DT) --- build/azure-pipelines/publish-types/update-types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/azure-pipelines/publish-types/update-types.ts b/build/azure-pipelines/publish-types/update-types.ts index c2677d446c6..9603726bebf 100644 --- a/build/azure-pipelines/publish-types/update-types.ts +++ b/build/azure-pipelines/publish-types/update-types.ts @@ -45,7 +45,7 @@ function repeat(str: string, times: number): string { } function convertTabsToSpaces(str: string): string { - return str.replace(/^\t+/gm, value => repeat(' ', value.length)); + return str.replace(/\t/gm, value => repeat(' ', value.length)); } function getNewFileContent(content: string, tag: string) { From da2d1f712c18155e81a0d7801b0d2025e84d97b9 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 13 Aug 2020 15:13:02 -0700 Subject: [PATCH 224/736] re #104583. --- src/vs/workbench/contrib/notebook/browser/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/constants.ts b/src/vs/workbench/contrib/notebook/browser/constants.ts index b6b383674be..77145e9098c 100644 --- a/src/vs/workbench/contrib/notebook/browser/constants.ts +++ b/src/vs/workbench/contrib/notebook/browser/constants.ts @@ -5,7 +5,7 @@ // Scrollable Element -export const SCROLLABLE_ELEMENT_PADDING_TOP = 20; +export const SCROLLABLE_ELEMENT_PADDING_TOP = 24; // Cell sizing related export const CELL_MARGIN = 8; From e1b316078b43603ae33ef8e604aaf033dcf5bb74 Mon Sep 17 00:00:00 2001 From: noecald Date: Thu, 13 Aug 2020 15:20:17 -0700 Subject: [PATCH 225/736] test for GitHub Action updated functionality --- .github/workflows/rich-navigation.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/rich-navigation.yml b/.github/workflows/rich-navigation.yml index cfc0381eead..38afb0d7f49 100644 --- a/.github/workflows/rich-navigation.yml +++ b/.github/workflows/rich-navigation.yml @@ -10,17 +10,11 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v2 - - name: Use Node.js - uses: actions/setup-node@v1 - name: Install dependencies run: yarn --frozen-lockfile env: CHILD_CONCURRENCY: 1 - - name: Install .NET Core 2.2 - uses: actions/setup-dotnet@v1.5.0 - with: - dotnet-version: 2.2 - - uses: microsoft/RichCodeNavIndexer@v0.1.2 + - uses: microsoft/RichCodeNavIndexer@v0.1 with: languages: typescript repo-token: ${{ secrets.GITHUB_TOKEN }} From 8323cbed48466666303d18b21b37d823c858031f Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 13 Aug 2020 15:20:54 -0700 Subject: [PATCH 226/736] fix #104583. --- src/vs/workbench/contrib/notebook/browser/constants.ts | 2 +- src/vs/workbench/contrib/notebook/browser/media/notebook.css | 4 ---- .../contrib/notebook/browser/notebookEditorWidget.ts | 5 ++++- .../contrib/notebook/browser/view/renderers/cellRenderer.ts | 4 ---- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/constants.ts b/src/vs/workbench/contrib/notebook/browser/constants.ts index 77145e9098c..b6b383674be 100644 --- a/src/vs/workbench/contrib/notebook/browser/constants.ts +++ b/src/vs/workbench/contrib/notebook/browser/constants.ts @@ -5,7 +5,7 @@ // Scrollable Element -export const SCROLLABLE_ELEMENT_PADDING_TOP = 24; +export const SCROLLABLE_ELEMENT_PADDING_TOP = 20; // Cell sizing related export const CELL_MARGIN = 8; diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 052f2c8b634..26d61482447 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -26,10 +26,6 @@ z-index: 100; } -.monaco-workbench .notebookOverlay .cell-list-container { - overflow: hidden; -} - .monaco-workbench .notebookOverlay .cell-list-container .overflowingContentWidgets > div { z-index: 600 !important; /* @rebornix: larger than the editor title bar */ diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 82d7940a114..5fc8b50dfd9 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -387,7 +387,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor DOM.addClass(this._body, 'cell-list-container'); this._dndController = this._register(new CellDragAndDropController(this, this._body)); - this._listTopCellToolbar = this._register(this.instantiationService.createInstance(ListTopCellToolbar, this, this._body)); const getScopedContextKeyService = (container?: HTMLElement) => this._list!.contextKeyService.createScoped(container); const renderers = [ this.instantiationService.createInstance(CodeCellRenderer, this, this._renderedEditors, this._dndController, getScopedContextKeyService), @@ -449,6 +448,9 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._register(this._list); this._register(combinedDisposable(...renderers)); + // top cell toolbar + this._listTopCellToolbar = this._register(this.instantiationService.createInstance(ListTopCellToolbar, this, this._list.rowsContainer)); + // transparent cover this._webviewTransparentCover = DOM.append(this._list.rowsContainer, $('.webview-cover')); this._webviewTransparentCover.style.display = 'none'; @@ -1965,4 +1967,5 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-shadow-container-bottom { top: ${CELL_BOTTOM_MARGIN}px; }`); collector.addRule(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-collapsed-part { margin-left: ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; height: ${COLLAPSED_INDICATOR_HEIGHT}px; }`); + collector.addRule(`.notebookOverlay .cell-list-top-cell-toolbar-container { top: -${SCROLLABLE_ELEMENT_PADDING_TOP}px }`); }); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 9c7bd78acf1..f75607d8563 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -1328,10 +1328,6 @@ export class ListTopCellToolbar extends Disposable { this._register(toolbar); - this._register(this.notebookEditor.onDidScroll((e) => { - this.topCellToolbar.style.top = `-${e.scrollTop}px`; - })); - this._register(this.notebookEditor.onDidChangeModel(() => { this._modelDisposables.clear(); From d8463db6b9b87c72d3f9af604dfa5d895ffc4384 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 13 Aug 2020 12:18:58 -0700 Subject: [PATCH 227/736] Pick up TS insiders --- 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 5577dde9099..3307d7709cb 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": "^4.0.1-rc" + "typescript": "^4.0.1-insiders.20200813" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 9e459157c1b..9a140984799 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@^4.0.1-rc: - version "4.0.1-rc" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.1-rc.tgz#8adc78223eae56fe71d906a5fa90c3543b07a677" - integrity sha512-TCkspT3dSKOykbzS3/WSK7pqU2h1d/lEO6i45Afm5Y3XNAEAo8YXTG3kHOQk/wFq/5uPyO1+X8rb/Q+g7UsxJw== +typescript@^4.0.1-insiders.20200813: + version "4.0.1-insiders.20200813" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.1-insiders.20200813.tgz#0b17335a7517023be0f1ce947052662ab2bde1f0" + integrity sha512-mTQPs9uyxv6jLEO5Z+LJpFUSQwx9KI3ZD+2Uv9e5O32Oz/16snCB5skBHw5k1PchsXOZCG6xcB902qmgjI0tWQ== From a76c649aaaa03a73bb60e43c288609b6f9d750b4 Mon Sep 17 00:00:00 2001 From: noecald Date: Thu, 13 Aug 2020 15:54:06 -0700 Subject: [PATCH 228/736] update to indexer version 0.1.4 --- .github/workflows/rich-navigation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rich-navigation.yml b/.github/workflows/rich-navigation.yml index 38afb0d7f49..6795cc35425 100644 --- a/.github/workflows/rich-navigation.yml +++ b/.github/workflows/rich-navigation.yml @@ -14,7 +14,7 @@ jobs: run: yarn --frozen-lockfile env: CHILD_CONCURRENCY: 1 - - uses: microsoft/RichCodeNavIndexer@v0.1 + - uses: microsoft/RichCodeNavIndexer@v0.1.4 with: languages: typescript repo-token: ${{ secrets.GITHUB_TOKEN }} From 35a1137a1e9a11d872d6443bcdc6195680d646e9 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 13 Aug 2020 16:03:04 -0700 Subject: [PATCH 229/736] Re #103881. --- .../contrib/notebook/browser/notebookServiceImpl.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index acc43755f64..918bcc458ed 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -426,7 +426,18 @@ export class NotebookService extends Disposable implements INotebookService, ICu cell.language, cell.cellKind, [], - cell.metadata + { + editable: cell.metadata?.editable, + runnable: cell.metadata?.runnable, + breakpointMargin: cell.metadata?.breakpointMargin, + hasExecutionOrder: cell.metadata?.hasExecutionOrder, + executionOrder: cell.metadata?.executionOrder, + statusMessage: cell.metadata?.statusMessage, + lastRunDuration: cell.metadata?.lastRunDuration, + inputCollapsed: cell.metadata?.inputCollapsed, + outputCollapsed: cell.metadata?.outputCollapsed, + custom: cell.metadata?.custom + } ); } else { return cell; From dde137236a030543c3ba947b9b2fd25b18358b54 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 13 Aug 2020 16:04:14 -0700 Subject: [PATCH 230/736] re #103881. --- .../workbench/contrib/notebook/browser/notebookServiceImpl.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 918bcc458ed..b5c95859442 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -431,9 +431,6 @@ export class NotebookService extends Disposable implements INotebookService, ICu runnable: cell.metadata?.runnable, breakpointMargin: cell.metadata?.breakpointMargin, hasExecutionOrder: cell.metadata?.hasExecutionOrder, - executionOrder: cell.metadata?.executionOrder, - statusMessage: cell.metadata?.statusMessage, - lastRunDuration: cell.metadata?.lastRunDuration, inputCollapsed: cell.metadata?.inputCollapsed, outputCollapsed: cell.metadata?.outputCollapsed, custom: cell.metadata?.custom From b990f95fed0e4724aca6a0b485e292f42770b88c Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 13 Aug 2020 16:12:25 -0700 Subject: [PATCH 231/736] Don't force focus into notebook when opening an empty notebook Fix #101622 --- .../contrib/notebook/browser/notebookEditorWidget.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 5fc8b50dfd9..669fc06564a 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -642,18 +642,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } } } - } else if (this._notebookViewModel && this._notebookViewModel.viewCells.length === 1 && this._notebookViewModel.viewCells[0].cellKind === CellKind.Code) { - // there is only one code cell in the document - const cell = this._notebookViewModel!.viewCells[0]; - if (cell.getTextLength() === 0) { - // the cell is empty, very likely a template cell, focus it - this.selectElement(cell); - await this.revealLineInCenterAsync(cell, 1); - const editor = this._renderedEditors.get(cell)!; - if (editor) { - editor.focus(); - } - } } } From 7d08008a07a81d7a77d06fef4633b96695e3e716 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 13 Aug 2020 16:13:48 -0700 Subject: [PATCH 232/736] re #103661. --- src/vs/workbench/contrib/notebook/browser/media/notebook.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 26d61482447..6d3c9fbc887 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -153,7 +153,7 @@ .monaco-workbench .notebookOverlay .output > div.foreground > .output-inner-container { width: 100%; - padding: 8px; + padding: 4px 8px; box-sizing: border-box; } From e0ae13f19e5e96bfefd79d85be9cd4884b9083cd Mon Sep 17 00:00:00 2001 From: noecald Date: Thu, 13 Aug 2020 16:17:50 -0700 Subject: [PATCH 233/736] remove patch version --- .github/workflows/rich-navigation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rich-navigation.yml b/.github/workflows/rich-navigation.yml index 6795cc35425..38afb0d7f49 100644 --- a/.github/workflows/rich-navigation.yml +++ b/.github/workflows/rich-navigation.yml @@ -14,7 +14,7 @@ jobs: run: yarn --frozen-lockfile env: CHILD_CONCURRENCY: 1 - - uses: microsoft/RichCodeNavIndexer@v0.1.4 + - uses: microsoft/RichCodeNavIndexer@v0.1 with: languages: typescript repo-token: ${{ secrets.GITHUB_TOKEN }} From 25499ff969e4a711c435737b957356e42a300421 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 13 Aug 2020 16:57:47 -0700 Subject: [PATCH 234/736] fix #101871. --- src/vs/workbench/api/browser/mainThreadNotebook.ts | 11 +++++++++-- src/vs/workbench/api/common/extHostNotebook.ts | 2 +- .../notebook/browser/notebookPureOutputRenderer.ts | 2 +- .../contrib/notebook/browser/notebookServiceImpl.ts | 11 ++++++++--- .../notebook/browser/view/renderers/codeCell.ts | 3 ++- .../contrib/notebook/common/notebookCommon.ts | 1 + .../contrib/notebook/common/notebookService.ts | 3 +++ 7 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index d9a61aa4f42..7fdf21501ff 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -415,7 +415,13 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo } async $registerNotebookRenderer(extension: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, preloads: UriComponents[]): Promise { - const renderer = new MainThreadNotebookRenderer(this._proxy, type, extension.id, URI.revive(extension.location), selectors, preloads.map(uri => URI.revive(uri))); + const staticContribution = this._notebookService.getContributedNotebookOutputRenderers(type); + + if (!staticContribution) { + throw new Error(`Notebook renderer for '${type}' is not statically registered.`); + } + + const renderer = new MainThreadNotebookRenderer(this._proxy, type, staticContribution.displayName, extension.id, URI.revive(extension.location), selectors, preloads.map(uri => URI.revive(uri))); this._notebookRenderers.set(type, renderer); this._notebookService.registerNotebookRenderer(type, renderer); } @@ -674,10 +680,11 @@ export class MainThreadNotebookRenderer implements INotebookRendererInfo { constructor( private readonly _proxy: ExtHostNotebookShape, readonly id: string, + public displayName: string, readonly extensionId: ExtensionIdentifier, readonly extensionLocation: URI, readonly selectors: INotebookMimeTypeSelector, - readonly preloads: URI[] + readonly preloads: URI[], ) { } diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 6452d5a3985..037b0c71e63 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -964,7 +964,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN filter: vscode.NotebookOutputSelector, renderer: vscode.NotebookOutputRenderer ): vscode.Disposable { - if (this._notebookKernels.has(type)) { + if (this._notebookOutputRenderers.has(type)) { throw new Error(`Notebook renderer for '${type}' already registered`); } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookPureOutputRenderer.ts b/src/vs/workbench/contrib/notebook/browser/notebookPureOutputRenderer.ts index c1f42f14d6c..af6c59fc4ed 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookPureOutputRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookPureOutputRenderer.ts @@ -19,7 +19,7 @@ export class PureNotebookOutputRenderer implements INotebookRendererInfo { public readonly preloads: URI[]; - constructor(public readonly id: string, extension: IExtensionDescription, entrypoint: string) { + constructor(public readonly id: string, public readonly displayName: string, extension: IExtensionDescription, entrypoint: string) { this.extensionId = extension.identifier; this.extensionLocation = extension.extensionLocation; this.preloads = [joinPath(extension.extensionLocation, entrypoint)]; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index b5c95859442..a45e3a397c0 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -298,7 +298,7 @@ export class NotebookService extends Disposable implements INotebookService, ICu })); if (notebookContribution.entrypoint) { - this._notebookRenderers.set(notebookContribution.viewType, new PureNotebookOutputRenderer(notebookContribution.viewType, extension.description, notebookContribution.entrypoint)); + this._notebookRenderers.set(notebookContribution.viewType, new PureNotebookOutputRenderer(notebookContribution.viewType, notebookContribution.displayName, extension.description, notebookContribution.entrypoint)); } } } @@ -547,6 +547,11 @@ export class NotebookService extends Disposable implements INotebookService, ICu registerNotebookRenderer(id: string, renderer: INotebookRendererInfo) { this._notebookRenderers.set(id, renderer); + const staticInfo = this.notebookRenderersInfoStore.get(id); + + if (staticInfo) { + + } } unregisterNotebookRenderer(id: string) { @@ -995,8 +1000,8 @@ export class NotebookService extends Disposable implements INotebookService, ICu return this.notebookProviderInfoStore.get(viewType); } - getContributedNotebookOutputRenderers(mimeType: string): readonly NotebookOutputRendererInfo[] { - return this.notebookRenderersInfoStore.getContributedRenderer(mimeType); + getContributedNotebookOutputRenderers(viewType: string): NotebookOutputRendererInfo | undefined { + return this.notebookRenderersInfoStore.get(viewType); } getNotebookProviderResourceRoots(): URI[] { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index c541cb9b136..ef7e8c202a1 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -558,7 +558,8 @@ export class CodeCell extends Disposable { const renderInfo = this.notebookService.getRendererInfo(renderId); if (renderInfo) { - return `${renderId} (${renderInfo.extensionId.value})`; + const displayName = renderInfo.displayName !== '' ? renderInfo.displayName : renderInfo.id; + return `${displayName} (${renderInfo.extensionId.value})`; } return nls.localize('builtinRenderInfo', "built-in"); diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 46c9f3f0ec5..319c8fd9130 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -113,6 +113,7 @@ export interface INotebookMimeTypeSelector { export interface INotebookRendererInfo { id: string; + displayName: string; extensionId: ExtensionIdentifier; extensionLocation: URI, preloads: URI[], diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index 9c6d199d7ed..87fad7fb364 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -16,6 +16,7 @@ import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/no import { CancellationToken } from 'vs/base/common/cancellation'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer'; export const INotebookService = createDecorator('notebookService'); @@ -61,7 +62,9 @@ export interface INotebookService { registerNotebookKernelProvider(provider: INotebookKernelProvider): IDisposable; getContributedNotebookKernels(viewType: string, resource: URI): readonly INotebookKernelInfo[]; getContributedNotebookKernels2(viewType: string, resource: URI, token: CancellationToken): Promise; + getContributedNotebookOutputRenderers(id: string): NotebookOutputRendererInfo | undefined; getRendererInfo(id: string): INotebookRendererInfo | undefined; + resolveNotebook(viewType: string, uri: URI, forceReload: boolean, editorId?: string, backupId?: string): Promise; getNotebookTextModel(uri: URI): NotebookTextModel | undefined; executeNotebook(viewType: string, uri: URI): Promise; From 4a316b0630e861710643daa6a7e9647cec6169f1 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 13 Aug 2020 17:09:57 -0700 Subject: [PATCH 235/736] keep cellRenderer clean --- .../notebook/browser/notebookBrowser.ts | 3 +- .../notebook/browser/notebookEditorWidget.ts | 3 +- .../browser/view/renderers/cellRenderer.ts | 332 +----------------- .../view/renderers/commonViewComponents.ts | 77 ++++ .../notebook/browser/view/renderers/dnd.ts | 278 +++++++++++++++ 5 files changed, 366 insertions(+), 327 deletions(-) create mode 100644 src/vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents.ts create mode 100644 src/vs/workbench/contrib/notebook/browser/view/renderers/dnd.ts diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index c51b949525b..102858f711e 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -19,7 +19,7 @@ import { Range } from 'vs/editor/common/core/range'; import { FindMatch, IReadonlyTextBuffer, ITextModel } from 'vs/editor/common/model'; import { ContextKeyExpr, RawContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer'; -import { CellLanguageStatusBarItem, RunStateRenderer, TimerRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer'; +import { RunStateRenderer, TimerRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer'; import { CellViewModel, IModelDecorationsChangeAccessor, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { CellKind, IProcessedOutput, IRenderOutput, NotebookCellMetadata, NotebookDocumentMetadata, INotebookKernelInfo, IEditor, INotebookKernelInfo2 } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -27,6 +27,7 @@ import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { IMenu } from 'vs/platform/actions/common/actions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { CellLanguageStatusBarItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents'; export const KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED = new RawContextKey('notebookFindWidgetFocused', false); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 669fc06564a..1f2d6291e32 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -33,7 +33,7 @@ import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/ import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList'; import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer'; import { BackLayerWebView } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView'; -import { CellDragAndDropController, CodeCellRenderer, MarkdownCellRenderer, NotebookCellListDelegate, ListTopCellToolbar } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer'; +import { CodeCellRenderer, MarkdownCellRenderer, NotebookCellListDelegate, ListTopCellToolbar } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { CellViewModel, IModelDecorationsChangeAccessor, INotebookEditorViewState, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; @@ -59,6 +59,7 @@ import { isMacintosh, isNative } from 'vs/base/common/platform'; import { getTitleBarStyle } from 'vs/platform/windows/common/windows'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ScrollEvent } from 'vs/base/common/scrollable'; +import { CellDragAndDropController } from 'vs/workbench/contrib/notebook/browser/view/renderers/dnd'; const $ = DOM.$; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index f75607d8563..40011006554 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -10,7 +10,6 @@ import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/lis import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IAction } from 'vs/base/common/actions'; -import { Delayer } from 'vs/base/common/async'; import { renderCodicons } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; @@ -26,7 +25,6 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ITextModel } from 'vs/editor/common/model'; import * as modes from 'vs/editor/common/modes'; import { tokenizeLineToHTML } from 'vs/editor/common/modes/textToHtmlTokenizer'; -import { IModeService } from 'vs/editor/common/services/modeService'; import { localize } from 'vs/nls'; import { MenuEntryActionViewItem, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenu, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; @@ -38,8 +36,8 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_TOP_MARGIN, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CancelCellAction, ChangeCellLanguageAction, ExecuteCellAction, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; -import { BaseCellRenderTemplate, CellEditState, CodeCellRenderTemplate, EXPAND_CELL_CONTENT_COMMAND_ID, ICellViewModel, INotebookCellList, INotebookEditor, isCodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CancelCellAction, ExecuteCellAction, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; +import { BaseCellRenderTemplate, CellEditState, CodeCellRenderTemplate, EXPAND_CELL_CONTENT_COMMAND_ID, ICellViewModel, INotebookEditor, isCodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys'; import { CellMenus } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellMenus'; import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/codeCell'; @@ -49,6 +47,8 @@ import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/vie import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { CellKind, NotebookCellMetadata, NotebookCellRunState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { createAndFillInActionBarActionsWithVerticalSeparators, VerticalSeparator, VerticalSeparatorViewItem } from './cellActionView'; +import { CodiconActionViewItem, CellLanguageStatusBarItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents'; +import { CellDragAndDropController, DRAGGING_CLASS } from 'vs/workbench/contrib/notebook/browser/view/renderers/dnd'; const $ = DOM.$; @@ -79,22 +79,6 @@ export class NotebookCellListDelegate implements IListVirtualDelegate HTMLElement; - -interface CellDragEvent { - browserEvent: DragEvent; - draggedOverCell: ICellViewModel; - cellTop: number; - cellHeight: number; - dragPosRatio: number; -} - -export class CellDragAndDropController extends Disposable { - // TODO@roblourens - should probably use dataTransfer here, but any dataTransfer set makes the editor think I am dropping a file, need - // to figure out how to prevent that - private currentDraggedCell: ICellViewModel | undefined; - - private listInsertionIndicator: HTMLElement; - - private list!: INotebookCellList; - - private isScrolling = false; - private scrollingDelayer: Delayer; - - constructor( - private readonly notebookEditor: INotebookEditor, - insertionIndicatorContainer: HTMLElement - ) { - super(); - - this.listInsertionIndicator = DOM.append(insertionIndicatorContainer, $('.cell-list-insertion-indicator')); - - this._register(domEvent(document.body, DOM.EventType.DRAG_START, true)(this.onGlobalDragStart.bind(this))); - this._register(domEvent(document.body, DOM.EventType.DRAG_END, true)(this.onGlobalDragEnd.bind(this))); - - const addCellDragListener = (eventType: string, handler: (e: CellDragEvent) => void) => { - this._register(DOM.addDisposableListener( - notebookEditor.getDomNode(), - eventType, - e => { - const cellDragEvent = this.toCellDragEvent(e); - if (cellDragEvent) { - handler(cellDragEvent); - } - })); - }; - - addCellDragListener(DOM.EventType.DRAG_OVER, event => { - event.browserEvent.preventDefault(); - this.onCellDragover(event); - }); - addCellDragListener(DOM.EventType.DROP, event => { - event.browserEvent.preventDefault(); - this.onCellDrop(event); - }); - addCellDragListener(DOM.EventType.DRAG_LEAVE, event => { - event.browserEvent.preventDefault(); - this.onCellDragLeave(event); - }); - - this.scrollingDelayer = new Delayer(200); - } - - setList(value: INotebookCellList) { - this.list = value; - - this.list.onWillScroll(e => { - if (!e.scrollTopChanged) { - return; - } - - this.setInsertIndicatorVisibility(false); - this.isScrolling = true; - this.scrollingDelayer.trigger(() => { - this.isScrolling = false; - }); - }); - } - - private setInsertIndicatorVisibility(visible: boolean) { - this.listInsertionIndicator.style.opacity = visible ? '1' : '0'; - } - - private toCellDragEvent(event: DragEvent): CellDragEvent | undefined { - const targetTop = this.notebookEditor.getDomNode().getBoundingClientRect().top; - const dragOffset = this.list.scrollTop + event.clientY - targetTop; - const draggedOverCell = this.list.elementAt(dragOffset); - if (!draggedOverCell) { - return undefined; - } - - const cellTop = this.list.getAbsoluteTopOfElement(draggedOverCell); - const cellHeight = this.list.elementHeight(draggedOverCell); - - const dragPosInElement = dragOffset - cellTop; - const dragPosRatio = dragPosInElement / cellHeight; - - return { - browserEvent: event, - draggedOverCell, - cellTop, - cellHeight, - dragPosRatio - }; - } - - clearGlobalDragState() { - this.notebookEditor.getDomNode().classList.remove(GLOBAL_DRAG_CLASS); - } - - private onGlobalDragStart() { - this.notebookEditor.getDomNode().classList.add(GLOBAL_DRAG_CLASS); - } - - private onGlobalDragEnd() { - this.notebookEditor.getDomNode().classList.remove(GLOBAL_DRAG_CLASS); - } - - private onCellDragover(event: CellDragEvent): void { - if (!event.browserEvent.dataTransfer) { - return; - } - - if (!this.currentDraggedCell) { - event.browserEvent.dataTransfer.dropEffect = 'none'; - return; - } - - if (this.isScrolling || this.currentDraggedCell === event.draggedOverCell) { - this.setInsertIndicatorVisibility(false); - return; - } - - const dropDirection = this.getDropInsertDirection(event); - const insertionIndicatorAbsolutePos = dropDirection === 'above' ? event.cellTop : event.cellTop + event.cellHeight; - const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + BOTTOM_CELL_TOOLBAR_HEIGHT / 2; - if (insertionIndicatorTop >= 0) { - this.listInsertionIndicator.style.top = `${insertionIndicatorTop}px`; - this.setInsertIndicatorVisibility(true); - } else { - this.setInsertIndicatorVisibility(false); - } - } - - private getDropInsertDirection(event: CellDragEvent): 'above' | 'below' { - return event.dragPosRatio < 0.5 ? 'above' : 'below'; - } - - private onCellDrop(event: CellDragEvent): void { - const draggedCell = this.currentDraggedCell!; - - if (this.isScrolling || this.currentDraggedCell === event.draggedOverCell) { - return; - } - - let draggedCells: ICellViewModel[] = [draggedCell]; - let draggedCellRange: [number, number] = [this.notebookEditor.viewModel!.getCellIndex(draggedCell), 1]; - - if (draggedCell.cellKind === CellKind.Markdown) { - const currCellIndex = this.notebookEditor.viewModel!.getCellIndex(draggedCell); - const nextVisibleCellIndex = this.notebookEditor.viewModel!.getNextVisibleCellIndex(currCellIndex); - - if (nextVisibleCellIndex > currCellIndex + 1) { - // folding ;) - draggedCells = this.notebookEditor.viewModel!.viewCells.slice(currCellIndex, nextVisibleCellIndex); - draggedCellRange = [currCellIndex, nextVisibleCellIndex - currCellIndex]; - } - } - - this.dragCleanup(); - - const isCopy = (event.browserEvent.ctrlKey && !platform.isMacintosh) || (event.browserEvent.altKey && platform.isMacintosh); - - const dropDirection = this.getDropInsertDirection(event); - const insertionIndicatorAbsolutePos = dropDirection === 'above' ? event.cellTop : event.cellTop + event.cellHeight; - const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + BOTTOM_CELL_TOOLBAR_HEIGHT / 2; - const editorHeight = this.notebookEditor.getDomNode().getBoundingClientRect().height; - if (insertionIndicatorTop < 0 || insertionIndicatorTop > editorHeight) { - // Ignore drop, insertion point is off-screen - return; - } - - if (isCopy) { - this.copyCells(draggedCells, event.draggedOverCell, dropDirection); - } else { - const viewModel = this.notebookEditor.viewModel!; - let originalToIdx = viewModel.getCellIndex(event.draggedOverCell); - if (dropDirection === 'below') { - const relativeToIndex = viewModel.getCellIndex(event.draggedOverCell); - const newIdx = viewModel.getNextVisibleCellIndex(relativeToIndex); - originalToIdx = newIdx; - } - - this.notebookEditor.moveCellsToIdx(draggedCellRange[0], draggedCellRange[1], originalToIdx); - } - } - - private onCellDragLeave(event: CellDragEvent): void { - if (!event.browserEvent.relatedTarget || !DOM.isAncestor(event.browserEvent.relatedTarget as HTMLElement, this.notebookEditor.getDomNode())) { - this.setInsertIndicatorVisibility(false); - } - } - - private dragCleanup(): void { - if (this.currentDraggedCell) { - this.currentDraggedCell.dragging = false; - this.currentDraggedCell = undefined; - } - - this.setInsertIndicatorVisibility(false); - } - - registerDragHandle(templateData: BaseCellRenderTemplate, cellRoot: HTMLElement, dragHandle: HTMLElement, dragImageProvider: DragImageProvider): void { - const container = templateData.container; - dragHandle.setAttribute('draggable', 'true'); - - templateData.disposables.add(domEvent(dragHandle, DOM.EventType.DRAG_END)(() => { - // Note, templateData may have a different element rendered into it by now - container.classList.remove(DRAGGING_CLASS); - this.dragCleanup(); - })); - - templateData.disposables.add(domEvent(dragHandle, DOM.EventType.DRAG_START)(event => { - if (!event.dataTransfer) { - return; - } - - this.currentDraggedCell = templateData.currentRenderedCell!; - this.currentDraggedCell.dragging = true; - - const dragImage = dragImageProvider(); - cellRoot.parentElement!.appendChild(dragImage); - event.dataTransfer.setDragImage(dragImage, 0, 0); - setTimeout(() => cellRoot.parentElement!.removeChild(dragImage!), 0); // Comment this out to debug drag image layout - - container.classList.add(DRAGGING_CLASS); - })); - } - - private copyCells(draggedCells: ICellViewModel[], ontoCell: ICellViewModel, direction: 'above' | 'below') { - this.notebookEditor.textModel!.pushStackElement('Copy Cells'); - let firstNewCell: ICellViewModel | undefined = undefined; - let firstNewCellState: CellEditState = CellEditState.Preview; - for (let i = 0; i < draggedCells.length; i++) { - const draggedCell = draggedCells[i]; - const newCell = this.notebookEditor.insertNotebookCell(ontoCell, draggedCell.cellKind, direction, draggedCell.getText()); - - if (newCell && !firstNewCell) { - firstNewCell = newCell; - firstNewCellState = draggedCell.editState; - } - } - - if (firstNewCell) { - this.notebookEditor.focusNotebookCell(firstNewCell, firstNewCellState === CellEditState.Editing ? 'editor' : 'container'); - } - - this.notebookEditor.textModel!.pushStackElement('Copy Cells'); - } -} - -export class CellLanguageStatusBarItem extends Disposable { - private readonly labelElement: HTMLElement; - - private cell: ICellViewModel | undefined; - private editor: INotebookEditor | undefined; - - private cellDisposables: DisposableStore; - - constructor( - readonly container: HTMLElement, - @IModeService private readonly modeService: IModeService, - @IInstantiationService private readonly instantiationService: IInstantiationService - ) { - super(); - this.labelElement = DOM.append(container, $('.cell-language-picker')); - this.labelElement.tabIndex = 0; - - this._register(DOM.addDisposableListener(this.labelElement, DOM.EventType.CLICK, () => { - this.instantiationService.invokeFunction(accessor => { - new ChangeCellLanguageAction().run(accessor, { notebookEditor: this.editor!, cell: this.cell! }); - }); - })); - this._register(this.cellDisposables = new DisposableStore()); - } - - update(cell: ICellViewModel, editor: INotebookEditor): void { - this.cellDisposables.clear(); - this.cell = cell; - this.editor = editor; - - this.render(); - this.cellDisposables.add(this.cell.model.onDidChangeLanguage(() => this.render())); - } - - private render(): void { - const modeId = this.cell?.cellKind === CellKind.Markdown ? 'markdown' : this.modeService.getModeIdForLanguageName(this.cell!.language) || this.cell!.language; - this.labelElement.textContent = this.modeService.getLanguageName(modeId) || this.modeService.getLanguageName('plaintext'); - } -} - class EditorTextRenderer { getRichText(editor: ICodeEditor, modelRange: Range): string | null { @@ -1132,7 +814,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende templateData.container.classList.remove(className); }); - this.commonRenderElement(element, index, templateData); + this.commonRenderElement(element, templateData); templateData.currentRenderedCell = element; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents.ts new file mode 100644 index 00000000000..d4905c60dcf --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents.ts @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as DOM from 'vs/base/browser/dom'; +import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { MenuItemAction } from 'vs/platform/actions/common/actions'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { renderCodicons } from 'vs/base/common/codicons'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { ICellViewModel, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ChangeCellLanguageAction } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; +import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; + +const $ = DOM.$; + +export class CodiconActionViewItem extends MenuEntryActionViewItem { + constructor( + readonly _action: MenuItemAction, + keybindingService: IKeybindingService, + notificationService: INotificationService, + contextMenuService: IContextMenuService + ) { + super(_action, keybindingService, notificationService, contextMenuService); + } + updateLabel(): void { + if (this.options.label && this.label) { + this.label.innerHTML = renderCodicons(this._commandAction.label ?? ''); + } + } +} + + +export class CellLanguageStatusBarItem extends Disposable { + private readonly labelElement: HTMLElement; + + private cell: ICellViewModel | undefined; + private editor: INotebookEditor | undefined; + + private cellDisposables: DisposableStore; + + constructor( + readonly container: HTMLElement, + @IModeService private readonly modeService: IModeService, + @IInstantiationService private readonly instantiationService: IInstantiationService + ) { + super(); + this.labelElement = DOM.append(container, $('.cell-language-picker')); + this.labelElement.tabIndex = 0; + + this._register(DOM.addDisposableListener(this.labelElement, DOM.EventType.CLICK, () => { + this.instantiationService.invokeFunction(accessor => { + new ChangeCellLanguageAction().run(accessor, { notebookEditor: this.editor!, cell: this.cell! }); + }); + })); + this._register(this.cellDisposables = new DisposableStore()); + } + + update(cell: ICellViewModel, editor: INotebookEditor): void { + this.cellDisposables.clear(); + this.cell = cell; + this.editor = editor; + + this.render(); + this.cellDisposables.add(this.cell.model.onDidChangeLanguage(() => this.render())); + } + + private render(): void { + const modeId = this.cell?.cellKind === CellKind.Markdown ? 'markdown' : this.modeService.getModeIdForLanguageName(this.cell!.language) || this.cell!.language; + this.labelElement.textContent = this.modeService.getLanguageName(modeId) || this.modeService.getLanguageName('plaintext'); + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/dnd.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/dnd.ts new file mode 100644 index 00000000000..57352dd8151 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/dnd.ts @@ -0,0 +1,278 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +import * as DOM from 'vs/base/browser/dom'; +import { domEvent } from 'vs/base/browser/event'; +import { Delayer } from 'vs/base/common/async'; +import { Disposable } from 'vs/base/common/lifecycle'; +import * as platform from 'vs/base/common/platform'; +import { BOTTOM_CELL_TOOLBAR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; +import { BaseCellRenderTemplate, CellEditState, ICellViewModel, INotebookCellList, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; + +const $ = DOM.$; + +export const DRAGGING_CLASS = 'cell-dragging'; +export const GLOBAL_DRAG_CLASS = 'global-drag-active'; + +type DragImageProvider = () => HTMLElement; + +interface CellDragEvent { + browserEvent: DragEvent; + draggedOverCell: ICellViewModel; + cellTop: number; + cellHeight: number; + dragPosRatio: number; +} + +export class CellDragAndDropController extends Disposable { + // TODO@roblourens - should probably use dataTransfer here, but any dataTransfer set makes the editor think I am dropping a file, need + // to figure out how to prevent that + private currentDraggedCell: ICellViewModel | undefined; + + private listInsertionIndicator: HTMLElement; + + private list!: INotebookCellList; + + private isScrolling = false; + private scrollingDelayer: Delayer; + + constructor( + private readonly notebookEditor: INotebookEditor, + insertionIndicatorContainer: HTMLElement + ) { + super(); + + this.listInsertionIndicator = DOM.append(insertionIndicatorContainer, $('.cell-list-insertion-indicator')); + + this._register(domEvent(document.body, DOM.EventType.DRAG_START, true)(this.onGlobalDragStart.bind(this))); + this._register(domEvent(document.body, DOM.EventType.DRAG_END, true)(this.onGlobalDragEnd.bind(this))); + + const addCellDragListener = (eventType: string, handler: (e: CellDragEvent) => void) => { + this._register(DOM.addDisposableListener( + notebookEditor.getDomNode(), + eventType, + e => { + const cellDragEvent = this.toCellDragEvent(e); + if (cellDragEvent) { + handler(cellDragEvent); + } + })); + }; + + addCellDragListener(DOM.EventType.DRAG_OVER, event => { + event.browserEvent.preventDefault(); + this.onCellDragover(event); + }); + addCellDragListener(DOM.EventType.DROP, event => { + event.browserEvent.preventDefault(); + this.onCellDrop(event); + }); + addCellDragListener(DOM.EventType.DRAG_LEAVE, event => { + event.browserEvent.preventDefault(); + this.onCellDragLeave(event); + }); + + this.scrollingDelayer = new Delayer(200); + } + + setList(value: INotebookCellList) { + this.list = value; + + this.list.onWillScroll(e => { + if (!e.scrollTopChanged) { + return; + } + + this.setInsertIndicatorVisibility(false); + this.isScrolling = true; + this.scrollingDelayer.trigger(() => { + this.isScrolling = false; + }); + }); + } + + private setInsertIndicatorVisibility(visible: boolean) { + this.listInsertionIndicator.style.opacity = visible ? '1' : '0'; + } + + private toCellDragEvent(event: DragEvent): CellDragEvent | undefined { + const targetTop = this.notebookEditor.getDomNode().getBoundingClientRect().top; + const dragOffset = this.list.scrollTop + event.clientY - targetTop; + const draggedOverCell = this.list.elementAt(dragOffset); + if (!draggedOverCell) { + return undefined; + } + + const cellTop = this.list.getAbsoluteTopOfElement(draggedOverCell); + const cellHeight = this.list.elementHeight(draggedOverCell); + + const dragPosInElement = dragOffset - cellTop; + const dragPosRatio = dragPosInElement / cellHeight; + + return { + browserEvent: event, + draggedOverCell, + cellTop, + cellHeight, + dragPosRatio + }; + } + + clearGlobalDragState() { + this.notebookEditor.getDomNode().classList.remove(GLOBAL_DRAG_CLASS); + } + + private onGlobalDragStart() { + this.notebookEditor.getDomNode().classList.add(GLOBAL_DRAG_CLASS); + } + + private onGlobalDragEnd() { + this.notebookEditor.getDomNode().classList.remove(GLOBAL_DRAG_CLASS); + } + + private onCellDragover(event: CellDragEvent): void { + if (!event.browserEvent.dataTransfer) { + return; + } + + if (!this.currentDraggedCell) { + event.browserEvent.dataTransfer.dropEffect = 'none'; + return; + } + + if (this.isScrolling || this.currentDraggedCell === event.draggedOverCell) { + this.setInsertIndicatorVisibility(false); + return; + } + + const dropDirection = this.getDropInsertDirection(event); + const insertionIndicatorAbsolutePos = dropDirection === 'above' ? event.cellTop : event.cellTop + event.cellHeight; + const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + BOTTOM_CELL_TOOLBAR_HEIGHT / 2; + if (insertionIndicatorTop >= 0) { + this.listInsertionIndicator.style.top = `${insertionIndicatorTop}px`; + this.setInsertIndicatorVisibility(true); + } else { + this.setInsertIndicatorVisibility(false); + } + } + + private getDropInsertDirection(event: CellDragEvent): 'above' | 'below' { + return event.dragPosRatio < 0.5 ? 'above' : 'below'; + } + + private onCellDrop(event: CellDragEvent): void { + const draggedCell = this.currentDraggedCell!; + + if (this.isScrolling || this.currentDraggedCell === event.draggedOverCell) { + return; + } + + let draggedCells: ICellViewModel[] = [draggedCell]; + let draggedCellRange: [number, number] = [this.notebookEditor.viewModel!.getCellIndex(draggedCell), 1]; + + if (draggedCell.cellKind === CellKind.Markdown) { + const currCellIndex = this.notebookEditor.viewModel!.getCellIndex(draggedCell); + const nextVisibleCellIndex = this.notebookEditor.viewModel!.getNextVisibleCellIndex(currCellIndex); + + if (nextVisibleCellIndex > currCellIndex + 1) { + // folding ;) + draggedCells = this.notebookEditor.viewModel!.viewCells.slice(currCellIndex, nextVisibleCellIndex); + draggedCellRange = [currCellIndex, nextVisibleCellIndex - currCellIndex]; + } + } + + this.dragCleanup(); + + const isCopy = (event.browserEvent.ctrlKey && !platform.isMacintosh) || (event.browserEvent.altKey && platform.isMacintosh); + + const dropDirection = this.getDropInsertDirection(event); + const insertionIndicatorAbsolutePos = dropDirection === 'above' ? event.cellTop : event.cellTop + event.cellHeight; + const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + BOTTOM_CELL_TOOLBAR_HEIGHT / 2; + const editorHeight = this.notebookEditor.getDomNode().getBoundingClientRect().height; + if (insertionIndicatorTop < 0 || insertionIndicatorTop > editorHeight) { + // Ignore drop, insertion point is off-screen + return; + } + + if (isCopy) { + this.copyCells(draggedCells, event.draggedOverCell, dropDirection); + } else { + const viewModel = this.notebookEditor.viewModel!; + let originalToIdx = viewModel.getCellIndex(event.draggedOverCell); + if (dropDirection === 'below') { + const relativeToIndex = viewModel.getCellIndex(event.draggedOverCell); + const newIdx = viewModel.getNextVisibleCellIndex(relativeToIndex); + originalToIdx = newIdx; + } + + this.notebookEditor.moveCellsToIdx(draggedCellRange[0], draggedCellRange[1], originalToIdx); + } + } + + private onCellDragLeave(event: CellDragEvent): void { + if (!event.browserEvent.relatedTarget || !DOM.isAncestor(event.browserEvent.relatedTarget as HTMLElement, this.notebookEditor.getDomNode())) { + this.setInsertIndicatorVisibility(false); + } + } + + private dragCleanup(): void { + if (this.currentDraggedCell) { + this.currentDraggedCell.dragging = false; + this.currentDraggedCell = undefined; + } + + this.setInsertIndicatorVisibility(false); + } + + registerDragHandle(templateData: BaseCellRenderTemplate, cellRoot: HTMLElement, dragHandle: HTMLElement, dragImageProvider: DragImageProvider): void { + const container = templateData.container; + dragHandle.setAttribute('draggable', 'true'); + + templateData.disposables.add(domEvent(dragHandle, DOM.EventType.DRAG_END)(() => { + // Note, templateData may have a different element rendered into it by now + container.classList.remove(DRAGGING_CLASS); + this.dragCleanup(); + })); + + templateData.disposables.add(domEvent(dragHandle, DOM.EventType.DRAG_START)(event => { + if (!event.dataTransfer) { + return; + } + + this.currentDraggedCell = templateData.currentRenderedCell!; + this.currentDraggedCell.dragging = true; + + const dragImage = dragImageProvider(); + cellRoot.parentElement!.appendChild(dragImage); + event.dataTransfer.setDragImage(dragImage, 0, 0); + setTimeout(() => cellRoot.parentElement!.removeChild(dragImage!), 0); // Comment this out to debug drag image layout + + container.classList.add(DRAGGING_CLASS); + })); + } + + private copyCells(draggedCells: ICellViewModel[], ontoCell: ICellViewModel, direction: 'above' | 'below') { + this.notebookEditor.textModel!.pushStackElement('Copy Cells'); + let firstNewCell: ICellViewModel | undefined = undefined; + let firstNewCellState: CellEditState = CellEditState.Preview; + for (let i = 0; i < draggedCells.length; i++) { + const draggedCell = draggedCells[i]; + const newCell = this.notebookEditor.insertNotebookCell(ontoCell, draggedCell.cellKind, direction, draggedCell.getText()); + + if (newCell && !firstNewCell) { + firstNewCell = newCell; + firstNewCellState = draggedCell.editState; + } + } + + if (firstNewCell) { + this.notebookEditor.focusNotebookCell(firstNewCell, firstNewCellState === CellEditState.Editing ? 'editor' : 'container'); + } + + this.notebookEditor.textModel!.pushStackElement('Copy Cells'); + } +} From 95b3a0a8a04fcdd41bbefb8f2136e61e4d92a749 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 13 Aug 2020 17:16:28 -0700 Subject: [PATCH 236/736] fix #101365. --- .../notebook/browser/view/renderers/commonViewComponents.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents.ts index d4905c60dcf..01db1c49f46 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { localize } from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { MenuItemAction } from 'vs/platform/actions/common/actions'; @@ -73,5 +74,6 @@ export class CellLanguageStatusBarItem extends Disposable { private render(): void { const modeId = this.cell?.cellKind === CellKind.Markdown ? 'markdown' : this.modeService.getModeIdForLanguageName(this.cell!.language) || this.cell!.language; this.labelElement.textContent = this.modeService.getLanguageName(modeId) || this.modeService.getLanguageName('plaintext'); + this.labelElement.title = localize('notebook.cell.status.language', "Select Cell Language Mode"); } } From bd08768fd310d34cdd4423a53e5f3ca77340fea5 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Thu, 13 Aug 2020 20:33:12 -0700 Subject: [PATCH 237/736] Bump up tasks aaccuracy (maany false positives) --- .github/classifier.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/classifier.json b/.github/classifier.json index c9196fc5691..33c179d3237 100644 --- a/.github/classifier.json +++ b/.github/classifier.json @@ -134,7 +134,7 @@ "snippets": {"assign": ["jrieken"]}, "splitview": {"assign": ["joaomoreno"]}, "suggest": {"assign": ["jrieken"]}, - "tasks": {"assign": ["alexr00"]}, + "tasks": {"assign": ["alexr00"], "accuracy": 0.85}, "telemetry": {"assign": []}, "themes": {"assign": ["aeschli"]}, "timeline": {"assign": ["eamodio"]}, From 613320a80c505de23f11bd7d46a3a1e7bf82ba8f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 14 Aug 2020 09:11:37 +0200 Subject: [PATCH 238/736] lifecycle - tweak error wording (#104593) --- .../lifecycle/electron-sandbox/lifecycleService.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts b/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts index 7071d3d1de0..883af93ee8e 100644 --- a/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts +++ b/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts @@ -135,16 +135,16 @@ export class NativeLifecycleService extends AbstractLifecycleService { let message: string; switch (reason) { case ShutdownReason.CLOSE: - message = localize('errorClose', "An unexpected error prevented the window from closing ({0}).", toErrorMessage(error)); + message = localize('errorClose', "An unexpected error was thrown while attempting to close the window ({0}).", toErrorMessage(error)); break; case ShutdownReason.QUIT: - message = localize('errorQuit', "An unexpected error prevented the application from closing ({0}).", toErrorMessage(error)); + message = localize('errorQuit', "An unexpected error was thrown while attempting to quit the application ({0}).", toErrorMessage(error)); break; case ShutdownReason.RELOAD: - message = localize('errorReload', "An unexpected error prevented the window from reloading ({0}).", toErrorMessage(error)); + message = localize('errorReload', "An unexpected error was thrown while attempting to reload the window ({0}).", toErrorMessage(error)); break; case ShutdownReason.LOAD: - message = localize('errorLoad', "An unexpected error prevented the window from changing it's workspace ({0}).", toErrorMessage(error)); + message = localize('errorLoad', "An unexpected error was thrown while attempting to change the workspace of the window ({0}).", toErrorMessage(error)); break; } From 194db6233f899ff61c171847473b3d5ac9f0f14c Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 13 Aug 2020 10:04:18 +0200 Subject: [PATCH 239/736] vscode does not open remote files that start with a dot. Fixes https://github.com/microsoft/vscode-remote-release/issues/3424 --- src/vs/platform/windows/electron-main/windowsMainService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index e441c8c4bb0..05243e74ee8 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -1134,7 +1134,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic if (forceOpenWorkspaceAsFile) { return { fileUri: uri, remoteAuthority }; } - } else if (posix.extname(anyPath).length > 0) { + } else if (posix.basename(anyPath).indexOf('.') !== -1) { // file name starts with a dot or has an file extension return { fileUri: uri, remoteAuthority }; } } From ae040391de70b2f223c4662d62c3702fe336ddfe Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 14 Aug 2020 10:52:28 +0200 Subject: [PATCH 240/736] actionBar: after a clear actions might be re-added to simply toggle some actions. We should preserve focus fixes #97128 --- src/vs/base/browser/ui/actionbar/actionbar.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index bbc0918db24..aef1d1c1e1d 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -285,6 +285,10 @@ export class ActionBar extends Disposable implements IActionRunner { index++; } }); + if (this.focusedItem) { + // After a clear actions might be re-added to simply toggle some actions. We should preserve focus #97128 + this.focus(this.focusedItem); + } } getWidth(index: number): number { From a2e2c862f063c35184d4e461c49027923516daa2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 14 Aug 2020 11:33:07 +0200 Subject: [PATCH 241/736] scorer - add test for strict prefix matching --- src/vs/base/test/common/fuzzyScorer.test.ts | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/vs/base/test/common/fuzzyScorer.test.ts b/src/vs/base/test/common/fuzzyScorer.test.ts index 2064ec5a075..301cabfc48e 100644 --- a/src/vs/base/test/common/fuzzyScorer.test.ts +++ b/src/vs/base/test/common/fuzzyScorer.test.ts @@ -985,6 +985,31 @@ suite('Fuzzy Scorer', () => { } }); + test('compareFilesByScore - prefer strict case prefix', function () { + const resourceA = URI.file('app/constants/color.js'); + const resourceB = URI.file('app/components/model/input/Color.js'); + + let query = 'Color'; + + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceB); + assert.equal(res[1], resourceA); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceB); + assert.equal(res[1], resourceA); + + query = 'color'; + + res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + }); + test('prepareQuery', () => { assert.equal(scorer.prepareQuery(' f*a ').normalized, 'fa'); assert.equal(scorer.prepareQuery('model Tester.ts').original, 'model Tester.ts'); From bb3c233e97d8e2d78d9fa97a5fea5274e8300ad1 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 14 Aug 2020 11:33:26 +0200 Subject: [PATCH 242/736] Fix when NPM view is shown #104591 --- extensions/npm/package.json | 2 +- extensions/npm/src/npmMain.ts | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/extensions/npm/package.json b/extensions/npm/package.json index 4dd9ee6a71c..aa478b26c9b 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -56,7 +56,6 @@ { "id": "npm", "name": "%view.name%", - "when": "npm:showScriptExplorer", "icon": "images/code.svg", "visibility": "hidden" } @@ -232,6 +231,7 @@ "type": "boolean", "default": false, "scope": "resource", + "deprecationMessage": "The NPM Script Explorer is now available in 'Views' menu in the Explorer in all folders.", "description": "%config.npm.enableScriptExplorer%" }, "npm.enableRunFromFolder": { diff --git a/extensions/npm/src/npmMain.ts b/extensions/npm/src/npmMain.ts index 764be6ea0fc..a92f554b759 100644 --- a/extensions/npm/src/npmMain.ts +++ b/extensions/npm/src/npmMain.ts @@ -8,7 +8,7 @@ import * as vscode from 'vscode'; import { addJSONProviders } from './features/jsonContributions'; import { runSelectedScript, selectAndRunScriptFromFolder } from './commands'; import { NpmScriptsTreeDataProvider } from './npmView'; -import { invalidateTasksCache, NpmTaskProvider, hasPackageJson } from './tasks'; +import { invalidateTasksCache, NpmTaskProvider } from './tasks'; import { invalidateHoverScriptsCache, NpmScriptHoverProvider } from './scriptHover'; let treeDataProvider: NpmScriptsTreeDataProvider | undefined; @@ -44,11 +44,6 @@ export async function activate(context: vscode.ExtensionContext): Promise registerHoverProvider(context); context.subscriptions.push(vscode.commands.registerCommand('npm.runSelectedScript', runSelectedScript)); - - if (await hasPackageJson()) { - vscode.commands.executeCommand('setContext', 'npm:showScriptExplorer', true); - } - context.subscriptions.push(vscode.commands.registerCommand('npm.runScriptFromFolder', selectAndRunScriptFromFolder)); } From 7e7955059ddd08ab8c1a2eb685992becb19000b0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 14 Aug 2020 13:21:14 +0200 Subject: [PATCH 243/736] web - first cut paste support --- src/vs/editor/contrib/clipboard/clipboard.ts | 30 ++++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/contrib/clipboard/clipboard.ts b/src/vs/editor/contrib/clipboard/clipboard.ts index 114004565c5..c8e82074897 100644 --- a/src/vs/editor/contrib/clipboard/clipboard.ts +++ b/src/vs/editor/contrib/clipboard/clipboard.ts @@ -16,6 +16,8 @@ import { MenuId } from 'vs/platform/actions/common/actions'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; const CLIPBOARD_CONTEXT_MENU_GROUP = '9_cutcopypaste'; @@ -23,10 +25,7 @@ const supportsCut = (platform.isNative || document.queryCommandSupported('cut')) const supportsCopy = (platform.isNative || document.queryCommandSupported('copy')); // IE and Edge have trouble with setting html content in clipboard const supportsCopyWithSyntaxHighlighting = (supportsCopy && !browser.isEdge); -// Chrome incorrectly returns true for document.queryCommandSupported('paste') -// when the paste feature is available but the calling script has insufficient -// privileges to actually perform the action -const supportsPaste = (platform.isNative || (!browser.isChrome && document.queryCommandSupported('paste'))); +const supportsPaste = true; function registerCommand(command: T): T { command.register(); @@ -167,8 +166,11 @@ function registerExecCommandImpl(target: MultiCommand | undefined, browserComman // 1. handle case when focus is in editor. target.addImplementation(10000, (accessor: ServicesAccessor, args: any) => { + const codeEditorService = accessor.get(ICodeEditorService); + const clipboardService = accessor.get(IClipboardService); + // Only if editor text focus (i.e. not if editor has widget focus). - const focusedEditor = accessor.get(ICodeEditorService).getFocusedCodeEditor(); + const focusedEditor = codeEditorService.getFocusedCodeEditor(); if (focusedEditor && focusedEditor.hasTextFocus()) { if (browserCommand === 'cut' || browserCommand === 'copy') { // Do not execute if there is no selection and empty selection clipboard is off @@ -178,7 +180,23 @@ function registerExecCommandImpl(target: MultiCommand | undefined, browserComman return true; } } - document.execCommand(browserCommand); + const result = document.execCommand(browserCommand); + // Web: certain browsers do not allow document.execCommand('paste') + // and as such we implement a workaround via the clipboard service + // Refs: https://github.com/microsoft/vscode/issues/82604 + if (!result && platform.isWeb && browserCommand === 'paste') { + (async () => { + const clipboardText = await clipboardService.readText(); + + const selection = focusedEditor.getSelection(); + if (selection) { + const edits = [EditOperation.insert(selection.getPosition(), clipboardText)]; + + focusedEditor.executeEdits('clipboard', edits); + } + })(); + return true; + } return true; } return false; From 6f5ebbaafb69eb29f76885b70d37641cb8deffa5 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 14 Aug 2020 13:53:08 +0200 Subject: [PATCH 244/736] use vscode-emmet-helper isnetad of vscode-emmet-helper2 --- extensions/emmet/extension.webpack.config.js | 2 +- extensions/emmet/package.json | 4 ++-- extensions/emmet/src/util.ts | 4 ++-- extensions/emmet/yarn.lock | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/extensions/emmet/extension.webpack.config.js b/extensions/emmet/extension.webpack.config.js index 96ada4c23a7..bfac2b59f47 100644 --- a/extensions/emmet/extension.webpack.config.js +++ b/extensions/emmet/extension.webpack.config.js @@ -21,6 +21,6 @@ module.exports = withDefaults({ filename: 'emmetNodeMain.js' }, externals: { - 'vscode-emmet-helper2': 'commonjs vscode-emmet-helper2', + 'vscode-emmet-helper': 'commonjs vscode-emmet-helper', }, }); diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index aadbba7edea..d02c19a78ac 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -422,7 +422,7 @@ "scripts": { "watch": "gulp watch-extension:emmet", "compile": "gulp compile-extension:emmet", - "deps": "yarn add vscode-emmet-helper2" + "deps": "yarn add vscode-emmet-helper" }, "devDependencies": { "@types/node": "^12.11.7", @@ -435,7 +435,7 @@ "@emmetio/html-matcher": "^0.3.3", "@emmetio/math-expression": "^0.1.1", "image-size": "^0.5.2", - "vscode-emmet-helper2": "^2.0.0-next.0", + "vscode-emmet-helper": "^2.0.0", "vscode-html-languageservice": "^3.0.3" } } diff --git a/extensions/emmet/src/util.ts b/extensions/emmet/src/util.ts index 0dc2a0e201d..8ef33e97a83 100644 --- a/extensions/emmet/src/util.ts +++ b/extensions/emmet/src/util.ts @@ -8,7 +8,7 @@ import parse from '@emmetio/html-matcher'; import parseStylesheet from '@emmetio/css-parser'; import { Node, HtmlNode, CssToken, Property, Rule, Stylesheet } from 'EmmetNode'; import { DocumentStreamReader } from './bufferStream'; -import * as EmmetHelper from 'vscode-emmet-helper2'; +import * as EmmetHelper from 'vscode-emmet-helper'; import { TextDocument as LSTextDocument } from 'vscode-html-languageservice'; let _emmetHelper: typeof EmmetHelper; @@ -26,7 +26,7 @@ export function getEmmetHelper() { // Lazy load vscode-emmet-helper instead of importing it // directly to reduce the start-up time of the extension if (!_emmetHelper) { - _emmetHelper = require('vscode-emmet-helper2'); + _emmetHelper = require('vscode-emmet-helper'); } updateEmmetExtensionsPath(); return _emmetHelper; diff --git a/extensions/emmet/yarn.lock b/extensions/emmet/yarn.lock index 06a8845658c..cd26963d81b 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-helper2@^2.0.0-next.0: - version "2.0.0-next.0" - resolved "https://registry.yarnpkg.com/vscode-emmet-helper2/-/vscode-emmet-helper2-2.0.0-next.0.tgz#86eb4c2e581a577e7eb56a51f662e72fb1c63b47" - integrity sha512-ccm6Fb5dkbdEDNLIAebWwVcb8X3AXZDsACLi4KYdCxyFSMV+pOoNokBf4rsu+rSYWNe+fMqxjXZs9z0G2CxPGg== +vscode-emmet-helper@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-2.0.0.tgz#0057ec2d4af8ac83b1f7937383714ffdc56fcc07" + integrity sha512-ytR+Ajxs6zeYI0b4bPsl+nPU8xm852piJUtIwO1ajp1Pw7lwn3VeR+f4ynmxOl9IjfOdF2kW9T/qIkeFbKLwYw== dependencies: "@emmetio/extract-abbreviation" "^0.2.0" jsonc-parser "^2.3.0" From b5d8e6e72c650a83e66e4938e2734c1830ca5ce7 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 14 Aug 2020 13:56:29 +0200 Subject: [PATCH 245/736] code.sh: add quotes to `export WSLENV` --- resources/win32/bin/code.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/win32/bin/code.sh b/resources/win32/bin/code.sh index 9f029e5522a..d86b6e0574a 100644 --- a/resources/win32/bin/code.sh +++ b/resources/win32/bin/code.sh @@ -37,7 +37,7 @@ else fi if [ $IN_WSL = true ]; then - export WSLENV=ELECTRON_RUN_AS_NODE/w:$WSLENV + export WSLENV="ELECTRON_RUN_AS_NODE/w:$WSLENV" CLI=$(wslpath -m "$VSCODE_PATH/resources/app/out/cli.js") # use the Remote WSL extension if installed From b3ec3fb424e33fd0dc8c539dc3b7336186a7570b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 14 Aug 2020 14:47:40 +0200 Subject: [PATCH 246/736] Cannot overwrite read only files anymore (#104651) --- .../services/textfile/electron-browser/nativeTextFileService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts b/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts index 05dfa205077..19add1a54b2 100644 --- a/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts +++ b/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts @@ -126,7 +126,7 @@ export class NativeTextFileService extends AbstractTextFileService { } try { - return super.write(resource, value, options); + return await super.write(resource, value, options); } catch (error) { // In case of permission denied, we need to check for readonly From 932a27d9e0c02a55780e1325a1072114dcc09ef9 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 14 Aug 2020 14:55:07 +0200 Subject: [PATCH 247/736] Unexpected hash collision. Fixes #86584 --- src/vs/base/common/hash.ts | 17 +++++++++++------ src/vs/base/test/common/hash.test.ts | 23 ++++++++++++++++++++--- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/vs/base/common/hash.ts b/src/vs/base/common/hash.ts index 4b47073d8e5..d5770516836 100644 --- a/src/vs/base/common/hash.ts +++ b/src/vs/base/common/hash.ts @@ -8,7 +8,12 @@ import * as strings from 'vs/base/common/strings'; /** * Return a hash value for an object. */ -export function hash(obj: any, hashVal = 0): number { +export function hash(obj: any): number { + return doHash(obj, 0); +} + + +export function doHash(obj: any, hashVal: number): number { switch (typeof obj) { case 'object': if (obj === null) { @@ -24,9 +29,9 @@ export function hash(obj: any, hashVal = 0): number { case 'number': return numberHash(obj, hashVal); case 'undefined': - return numberHash(0, 937); + return numberHash(937, hashVal); default: - return numberHash(0, 617); + return numberHash(617, hashVal); } } @@ -48,14 +53,14 @@ export function stringHash(s: string, hashVal: number) { function arrayHash(arr: any[], initialHashVal: number): number { initialHashVal = numberHash(104579, initialHashVal); - return arr.reduce((hashVal, item) => hash(item, hashVal), initialHashVal); + return arr.reduce((hashVal, item) => doHash(item, hashVal), initialHashVal); } function objectHash(obj: any, initialHashVal: number): number { initialHashVal = numberHash(181387, initialHashVal); return Object.keys(obj).sort().reduce((hashVal, key) => { hashVal = stringHash(key, hashVal); - return hash(obj[key], hashVal); + return doHash(obj[key], hashVal); }, initialHashVal); } @@ -68,7 +73,7 @@ export class Hasher { } hash(obj: any): number { - this._value = hash(obj, this._value); + this._value = doHash(obj, this._value); return this._value; } } diff --git a/src/vs/base/test/common/hash.test.ts b/src/vs/base/test/common/hash.test.ts index 3225caf7b23..9484fa616dc 100644 --- a/src/vs/base/test/common/hash.test.ts +++ b/src/vs/base/test/common/hash.test.ts @@ -32,12 +32,18 @@ suite('Hash', () => { assert.equal(hash([1, 2, 3]), hash([1, 2, 3])); assert.equal(hash(['foo', 'bar']), hash(['foo', 'bar'])); assert.equal(hash([]), hash([])); + assert.equal(hash([]), hash(new Array())); assert.notEqual(hash(['foo', 'bar']), hash(['bar', 'foo'])); assert.notEqual(hash(['foo', 'bar']), hash(['bar', 'foo', null])); + assert.notEqual(hash(['foo', 'bar', null]), hash(['bar', 'foo', null])); + assert.notEqual(hash(['foo', 'bar']), hash(['bar', 'foo', undefined])); + assert.notEqual(hash(['foo', 'bar', undefined]), hash(['bar', 'foo', undefined])); + assert.notEqual(hash(['foo', 'bar', null]), hash(['foo', 'bar', undefined])); }); test('object', () => { assert.equal(hash({}), hash({})); + assert.equal(hash({}), hash(Object.create(null))); assert.equal(hash({ 'foo': 'bar' }), hash({ 'foo': 'bar' })); assert.equal(hash({ 'foo': 'bar', 'foo2': undefined }), hash({ 'foo2': undefined, 'foo': 'bar' })); assert.notEqual(hash({ 'foo': 'bar' }), hash({ 'foo': 'bar2' })); @@ -45,14 +51,25 @@ suite('Hash', () => { }); test('array - unexpected collision', function () { - this.skip(); const a = hash([undefined, undefined, undefined, undefined, undefined]); const b = hash([undefined, undefined, 'HHHHHH', [{ line: 0, character: 0 }, { line: 0, character: 0 }], undefined]); - // console.log(a); - // console.log(b); assert.notEqual(a, b); }); + test('all different', () => { + const candidates: any[] = [ + null, undefined, {}, [], 0, false, true, '', ' ', [null], [undefined], [undefined, undefined], { '': undefined }, { [' ']: undefined } + ]; + const hashes: number[] = candidates.map(hash); + for (let i = 0; i < hashes.length; i++) { + assert.equal(hashes[i], hash(candidates[i])); // verify that repeated invocation returns the same hash + for (let k = i + 1; k < hashes.length; k++) { + assert.notEqual(hashes[i], hashes[k], `Same hash ${hashes[i]} for ${JSON.stringify(candidates[i])} ans ${JSON.stringify(candidates[k])}`); + } + } + }); + + function checkSHA1(strings: string[], expected: string) { const hash = new StringSHA1(); for (const str of strings) { From a82932c1b8e6ce56d6022d922aac19a80e0f15ec Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 14 Aug 2020 15:07:50 +0200 Subject: [PATCH 248/736] Add when to task commands --- .../tasks/browser/runAutomaticTasks.ts | 26 +-- .../tasks/browser/task.contribution.ts | 164 ++++++++++++++---- 2 files changed, 150 insertions(+), 40 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts b/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts index 4866dd1e823..0bbcafa4ff3 100644 --- a/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts +++ b/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts @@ -11,8 +11,10 @@ import { forEach } from 'vs/base/common/collections'; import { RunOnOptions, Task, TaskRunSource } from 'vs/workbench/contrib/tasks/common/tasks'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -import { Action } from 'vs/base/common/actions'; import { IQuickPickItem, IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { Action2 } from 'vs/platform/actions/common/actions'; +import { TASKS_CATEGORY } from 'vs/workbench/contrib/tasks/browser/task.contribution'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; const ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE = 'tasks.run.allowAutomatic'; @@ -132,27 +134,29 @@ export class RunAutomaticTasks extends Disposable implements IWorkbenchContribut } -export class ManageAutomaticTaskRunning extends Action { +export class ManageAutomaticTaskRunning extends Action2 { public static readonly ID = 'workbench.action.tasks.manageAutomaticRunning'; public static readonly LABEL = nls.localize('workbench.action.tasks.manageAutomaticRunning', "Manage Automatic Tasks in Folder"); - constructor( - id: string, label: string, - @IStorageService private readonly storageService: IStorageService, - @IQuickInputService private readonly quickInputService: IQuickInputService - ) { - super(id, label); + constructor() { + super({ + id: ManageAutomaticTaskRunning.ID, + title: ManageAutomaticTaskRunning.LABEL, + category: TASKS_CATEGORY + }); } - public async run(): Promise { + public async run(accessor: ServicesAccessor): Promise { + const quickInputService = accessor.get(IQuickInputService); + const storageService = accessor.get(IStorageService); const allowItem: IQuickPickItem = { label: nls.localize('workbench.action.tasks.allowAutomaticTasks', "Allow Automatic Tasks in Folder") }; const disallowItem: IQuickPickItem = { label: nls.localize('workbench.action.tasks.disallowAutomaticTasks', "Disallow Automatic Tasks in Folder") }; - const value = await this.quickInputService.pick([allowItem, disallowItem], { canPickMany: false }); + const value = await quickInputService.pick([allowItem, disallowItem], { canPickMany: false }); if (!value) { return; } - this.storageService.store(ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE, value === allowItem, StorageScope.WORKSPACE); + storageService.store(ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE, value === allowItem, StorageScope.WORKSPACE); } } diff --git a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts index 6418a23ec93..e35b7641f36 100644 --- a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts @@ -8,7 +8,7 @@ import * as nls from 'vs/nls'; import { Disposable } from 'vs/base/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { MenuRegistry, MenuId, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { MenuRegistry, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { ProblemMatcherRegistry } from 'vs/workbench/contrib/tasks/common/problemMatcher'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; @@ -21,10 +21,9 @@ import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor, IStatus import { IOutputChannelRegistry, Extensions as OutputExt } from 'vs/workbench/services/output/common/output'; import { TaskEvent, TaskEventKind, TaskGroup, TASK_RUNNING_STATE } from 'vs/workbench/contrib/tasks/common/tasks'; -import { ITaskService } from 'vs/workbench/contrib/tasks/common/taskService'; +import { ITaskService, ProcessExecutionSupportedContext, ShellExecutionSupportedContext } from 'vs/workbench/contrib/tasks/common/taskService'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { RunAutomaticTasks, ManageAutomaticTaskRunning } from 'vs/workbench/contrib/tasks/browser/runAutomaticTasks'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; @@ -36,14 +35,23 @@ import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'v import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys'; import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/platform/quickinput/common/quickAccess'; import { TasksQuickAccessProvider } from 'vs/workbench/contrib/tasks/browser/tasksQuickAccess'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -let tasksCategory = { value: nls.localize('tasksCategory', "Tasks"), original: 'Tasks' }; +export const TASKS_CATEGORY = { value: nls.localize('tasksCategory', "Tasks"), original: 'Tasks' }; +const SHOW_TASKS_COMMANDS_CONTEXT = ContextKeyExpr.or(ShellExecutionSupportedContext, ProcessExecutionSupportedContext); const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(RunAutomaticTasks, LifecyclePhase.Eventually); -const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); -actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ManageAutomaticTaskRunning), 'Tasks: Manage Automatic Tasks in Folder', tasksCategory.value); +registerAction2(ManageAutomaticTaskRunning); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: ManageAutomaticTaskRunning.ID, + title: ManageAutomaticTaskRunning.LABEL, + category: TASKS_CATEGORY + }, + when: SHOW_TASKS_COMMANDS_CONTEXT +}); export class TaskStatusBarContributions extends Disposable implements IWorkbenchContribution { private runningTasksStatusItem: IStatusbarEntryAccessor | undefined; @@ -160,7 +168,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { id: 'workbench.action.tasks.runTask', title: nls.localize({ key: 'miRunTask', comment: ['&& denotes a mnemonic'] }, "&&Run Task...") }, - order: 1 + order: 1, + when: SHOW_TASKS_COMMANDS_CONTEXT }); MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { @@ -169,7 +178,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { id: 'workbench.action.tasks.build', title: nls.localize({ key: 'miBuildTask', comment: ['&& denotes a mnemonic'] }, "Run &&Build Task...") }, - order: 2 + order: 2, + when: SHOW_TASKS_COMMANDS_CONTEXT }); // Manage Tasks @@ -180,7 +190,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { id: 'workbench.action.tasks.showTasks', title: nls.localize({ key: 'miRunningTask', comment: ['&& denotes a mnemonic'] }, "Show Runnin&&g Tasks...") }, - order: 1 + order: 1, + when: SHOW_TASKS_COMMANDS_CONTEXT }); MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { @@ -190,7 +201,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { id: 'workbench.action.tasks.restartTask', title: nls.localize({ key: 'miRestartTask', comment: ['&& denotes a mnemonic'] }, "R&&estart Running Task...") }, - order: 2 + order: 2, + when: SHOW_TASKS_COMMANDS_CONTEXT }); MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { @@ -200,7 +212,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { id: 'workbench.action.tasks.terminate', title: nls.localize({ key: 'miTerminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task...") }, - order: 3 + order: 3, + when: SHOW_TASKS_COMMANDS_CONTEXT }); // Configure Tasks @@ -210,7 +223,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { id: 'workbench.action.tasks.configureTaskRunner', title: nls.localize({ key: 'miConfigureTask', comment: ['&& denotes a mnemonic'] }, "&&Configure Tasks...") }, - order: 1 + order: 1, + when: SHOW_TASKS_COMMANDS_CONTEXT }); MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { @@ -219,31 +233,123 @@ MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { id: 'workbench.action.tasks.configureDefaultBuildTask', title: nls.localize({ key: 'miConfigureBuildTask', comment: ['&& denotes a mnemonic'] }, "Configure De&&fault Build Task...") }, - order: 2 + order: 2, + when: SHOW_TASKS_COMMANDS_CONTEXT }); -MenuRegistry.appendMenuItem(MenuId.CommandPalette, ({ +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: 'workbench.action.tasks.openWorkspaceFileTasks', title: { value: nls.localize('workbench.action.tasks.openWorkspaceFileTasks', "Open Workspace Tasks"), original: 'Open Workspace Tasks' }, - category: tasksCategory + category: TASKS_CATEGORY }, - when: WorkbenchStateContext.isEqualTo('workspace') -})); + when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), SHOW_TASKS_COMMANDS_CONTEXT) +}); -MenuRegistry.addCommand({ id: ConfigureTaskAction.ID, title: { value: ConfigureTaskAction.TEXT, original: 'Configure Task' }, category: tasksCategory }); -MenuRegistry.addCommand({ id: 'workbench.action.tasks.showLog', title: { value: nls.localize('ShowLogAction.label', "Show Task Log"), original: 'Show Task Log' }, category: tasksCategory }); -MenuRegistry.addCommand({ id: 'workbench.action.tasks.runTask', title: { value: nls.localize('RunTaskAction.label', "Run Task"), original: 'Run Task' }, category: tasksCategory }); -MenuRegistry.addCommand({ id: 'workbench.action.tasks.reRunTask', title: { value: nls.localize('ReRunTaskAction.label', "Rerun Last Task"), original: 'Rerun Last Task' }, category: tasksCategory }); -MenuRegistry.addCommand({ id: 'workbench.action.tasks.restartTask', title: { value: nls.localize('RestartTaskAction.label', "Restart Running Task"), original: 'Restart Running Task' }, category: tasksCategory }); -MenuRegistry.addCommand({ id: 'workbench.action.tasks.showTasks', title: { value: nls.localize('ShowTasksAction.label', "Show Running Tasks"), original: 'Show Running Tasks' }, category: tasksCategory }); -MenuRegistry.addCommand({ id: 'workbench.action.tasks.terminate', title: { value: nls.localize('TerminateAction.label', "Terminate Task"), original: 'Terminate Task' }, category: tasksCategory }); -MenuRegistry.addCommand({ id: 'workbench.action.tasks.build', title: { value: nls.localize('BuildAction.label', "Run Build Task"), original: 'Run Build Task' }, category: tasksCategory }); -MenuRegistry.addCommand({ id: 'workbench.action.tasks.test', title: { value: nls.localize('TestAction.label', "Run Test Task"), original: 'Run Test Task' }, category: tasksCategory }); -MenuRegistry.addCommand({ id: 'workbench.action.tasks.configureDefaultBuildTask', title: { value: nls.localize('ConfigureDefaultBuildTask.label', "Configure Default Build Task"), original: 'Configure Default Build Task' }, category: tasksCategory }); -MenuRegistry.addCommand({ id: 'workbench.action.tasks.configureDefaultTestTask', title: { value: nls.localize('ConfigureDefaultTestTask.label', "Configure Default Test Task"), original: 'Configure Default Test Task' }, category: tasksCategory }); -MenuRegistry.addCommand({ id: 'workbench.action.tasks.openUserTasks', title: { value: nls.localize('workbench.action.tasks.openUserTasks', "Open User Tasks"), original: 'Open User Tasks' }, category: tasksCategory }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: ConfigureTaskAction.ID, + title: { value: ConfigureTaskAction.TEXT, original: 'Configure Task' }, + category: TASKS_CATEGORY + }, + when: SHOW_TASKS_COMMANDS_CONTEXT +}); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: 'workbench.action.tasks.showLog', + title: { value: nls.localize('ShowLogAction.label', "Show Task Log"), original: 'Show Task Log' }, + category: TASKS_CATEGORY + }, + when: SHOW_TASKS_COMMANDS_CONTEXT +}); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: 'workbench.action.tasks.runTask', + title: { value: nls.localize('RunTaskAction.label', "Run Task"), original: 'Run Task' }, + category: TASKS_CATEGORY + } +}); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: 'workbench.action.tasks.reRunTask', + title: { value: nls.localize('ReRunTaskAction.label', "Rerun Last Task"), original: 'Rerun Last Task' }, + category: TASKS_CATEGORY + }, + when: SHOW_TASKS_COMMANDS_CONTEXT +}); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: 'workbench.action.tasks.restartTask', + title: { value: nls.localize('RestartTaskAction.label', "Restart Running Task"), original: 'Restart Running Task' }, + category: TASKS_CATEGORY + }, + when: SHOW_TASKS_COMMANDS_CONTEXT +}); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: 'workbench.action.tasks.showTasks', + title: { value: nls.localize('ShowTasksAction.label', "Show Running Tasks"), original: 'Show Running Tasks' }, + category: TASKS_CATEGORY + }, + when: SHOW_TASKS_COMMANDS_CONTEXT +}); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: 'workbench.action.tasks.terminate', + title: { value: nls.localize('TerminateAction.label', "Terminate Task"), original: 'Terminate Task' }, + category: TASKS_CATEGORY + }, + when: SHOW_TASKS_COMMANDS_CONTEXT +}); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: 'workbench.action.tasks.build', + title: { value: nls.localize('BuildAction.label', "Run Build Task"), original: 'Run Build Task' }, + category: TASKS_CATEGORY + }, + when: SHOW_TASKS_COMMANDS_CONTEXT +}); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: 'workbench.action.tasks.test', + title: { value: nls.localize('TestAction.label', "Run Test Task"), original: 'Run Test Task' }, + category: TASKS_CATEGORY + }, + when: SHOW_TASKS_COMMANDS_CONTEXT +}); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: 'workbench.action.tasks.configureDefaultBuildTask', + title: { + value: nls.localize('ConfigureDefaultBuildTask.label', "Configure Default Build Task"), + original: 'Configure Default Build Task' + }, + category: TASKS_CATEGORY + }, + when: SHOW_TASKS_COMMANDS_CONTEXT +}); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: 'workbench.action.tasks.configureDefaultTestTask', + title: { + value: nls.localize('ConfigureDefaultTestTask.label', "Configure Default Test Task"), + original: 'Configure Default Test Task' + }, + category: TASKS_CATEGORY + }, + when: SHOW_TASKS_COMMANDS_CONTEXT +}); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: 'workbench.action.tasks.openUserTasks', + title: { + value: nls.localize('workbench.action.tasks.openUserTasks', "Open User Tasks"), + original: 'Open User Tasks' + }, category: TASKS_CATEGORY + }, + when: SHOW_TASKS_COMMANDS_CONTEXT +}); // MenuRegistry.addCommand( { id: 'workbench.action.tasks.rebuild', title: nls.localize('RebuildAction.label', 'Run Rebuild Task'), category: tasksCategory }); // MenuRegistry.addCommand( { id: 'workbench.action.tasks.clean', title: nls.localize('CleanAction.label', 'Run Clean Task'), category: tasksCategory }); From c692ca0dce50434da956084bbc6186154bf07974 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 14 Aug 2020 15:25:32 +0200 Subject: [PATCH 249/736] more hash tests --- src/vs/base/test/common/hash.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/base/test/common/hash.test.ts b/src/vs/base/test/common/hash.test.ts index 9484fa616dc..59163e78523 100644 --- a/src/vs/base/test/common/hash.test.ts +++ b/src/vs/base/test/common/hash.test.ts @@ -58,7 +58,8 @@ suite('Hash', () => { test('all different', () => { const candidates: any[] = [ - null, undefined, {}, [], 0, false, true, '', ' ', [null], [undefined], [undefined, undefined], { '': undefined }, { [' ']: undefined } + null, undefined, {}, [], 0, false, true, '', ' ', [null], [undefined], [undefined, undefined], { '': undefined }, { [' ']: undefined }, + 'ab', 'ba', ['ab'] ]; const hashes: number[] = candidates.map(hash); for (let i = 0; i < hashes.length; i++) { From 1d3bea8480730d9aa8edce0139f1972e2757467b Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 14 Aug 2020 15:26:31 +0200 Subject: [PATCH 250/736] fix typo --- src/vs/base/test/common/hash.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/test/common/hash.test.ts b/src/vs/base/test/common/hash.test.ts index 59163e78523..b5074f4ffa5 100644 --- a/src/vs/base/test/common/hash.test.ts +++ b/src/vs/base/test/common/hash.test.ts @@ -65,7 +65,7 @@ suite('Hash', () => { for (let i = 0; i < hashes.length; i++) { assert.equal(hashes[i], hash(candidates[i])); // verify that repeated invocation returns the same hash for (let k = i + 1; k < hashes.length; k++) { - assert.notEqual(hashes[i], hashes[k], `Same hash ${hashes[i]} for ${JSON.stringify(candidates[i])} ans ${JSON.stringify(candidates[k])}`); + assert.notEqual(hashes[i], hashes[k], `Same hash ${hashes[i]} for ${JSON.stringify(candidates[i])} and ${JSON.stringify(candidates[k])}`); } } }); From 8418e06a5bde8f26f1cd5008336ecc6b6d6f540b Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 14 Aug 2020 16:28:14 +0200 Subject: [PATCH 251/736] Resolve customExecution task definition before going to exthost Part of #81007 --- src/vs/vscode.proposed.d.ts | 2 +- .../workbench/api/browser/mainThreadTask.ts | 10 +++++-- .../workbench/api/common/extHost.protocol.ts | 2 +- src/vs/workbench/api/common/extHostTask.ts | 8 ++---- src/vs/workbench/api/node/extHostTask.ts | 27 ------------------- 5 files changed, 12 insertions(+), 37 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index c753ef72f24..10e4324721b 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -995,7 +995,7 @@ declare module 'vscode' { * [Pseudoterminal.onDidClose](#Pseudoterminal.onDidClose). * @param callback The callback that will be called when the task is started by a user. */ - constructor(callback: (resolvedDefinition?: TaskDefinition) => Thenable); + constructor(callback: (resolvedDefinition: TaskDefinition) => Thenable); } //#endregion diff --git a/src/vs/workbench/api/browser/mainThreadTask.ts b/src/vs/workbench/api/browser/mainThreadTask.ts index 586acc3ab8d..d94b7f3dfcb 100644 --- a/src/vs/workbench/api/browser/mainThreadTask.ts +++ b/src/vs/workbench/api/browser/mainThreadTask.ts @@ -414,10 +414,16 @@ export class MainThreadTask implements MainThreadTaskShape { ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTask); this._providers = new Map(); - this._taskService.onDidStateChange((event: TaskEvent) => { + this._taskService.onDidStateChange(async (event: TaskEvent) => { const task = event.__task!; if (event.kind === TaskEventKind.Start) { - this._proxy.$onDidStartTask(TaskExecutionDTO.from(task.getTaskExecution()), event.terminalId!); + const execution = TaskExecutionDTO.from(task.getTaskExecution()); + let resolvedDefinition: TaskDefinitionDTO | undefined; + if (execution.task && execution.task.execution && CustomExecutionDTO.is(execution.task.execution)) { + resolvedDefinition = await this._configurationResolverService.resolveWithInteractionReplace(task.getWorkspaceFolder(), + execution.task.definition, 'tasks'); + } + this._proxy.$onDidStartTask(execution, event.terminalId!, resolvedDefinition); } else if (event.kind === TaskEventKind.ProcessStarted) { this._proxy.$onDidStartTaskProcess(TaskProcessStartedDTO.from(task.getTaskExecution(), event.processId!)); } else if (event.kind === TaskEventKind.ProcessEnded) { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 1c7e49f5c97..fcee75d3127 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1448,7 +1448,7 @@ export interface ExtHostSCMShape { export interface ExtHostTaskShape { $provideTasks(handle: number, validTypes: { [key: string]: boolean; }): Thenable; $resolveTask(handle: number, taskDTO: tasks.TaskDTO): Thenable; - $onDidStartTask(execution: tasks.TaskExecutionDTO, terminalId: number): void; + $onDidStartTask(execution: tasks.TaskExecutionDTO, terminalId: number, resolvedDefinition?: tasks.TaskDefinitionDTO): void; $onDidStartTaskProcess(value: tasks.TaskProcessStartedDTO): void; $onDidEndTaskProcess(value: tasks.TaskProcessEndedDTO): void; $OnDidEndTask(execution: tasks.TaskExecutionDTO): void; diff --git a/src/vs/workbench/api/common/extHostTask.ts b/src/vs/workbench/api/common/extHostTask.ts index 610e10ec65e..f77a8626067 100644 --- a/src/vs/workbench/api/common/extHostTask.ts +++ b/src/vs/workbench/api/common/extHostTask.ts @@ -475,11 +475,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape, IExtHostTask return this._onDidExecuteTask.event; } - protected async resolveDefinition(uri: number | UriComponents | undefined, definition: vscode.TaskDefinition | undefined): Promise { - return definition; - } - - public async $onDidStartTask(execution: tasks.TaskExecutionDTO, terminalId: number): Promise { + public async $onDidStartTask(execution: tasks.TaskExecutionDTO, terminalId: number, resolvedDefinition?: tasks.TaskDefinitionDTO): Promise { const customExecution: types.CustomExecution | undefined = this._providedCustomExecutions2.get(execution.id); if (customExecution) { if (this._activeCustomExecutions2.get(execution.id) !== undefined) { @@ -488,7 +484,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape, IExtHostTask // Clone the custom execution to keep the original untouched. This is important for multiple runs of the same task. this._activeCustomExecutions2.set(execution.id, customExecution); - this._terminalService.attachPtyToTerminal(terminalId, await customExecution.callback(await this.resolveDefinition(execution.task?.source.scope, execution.task?.definition))); + this._terminalService.attachPtyToTerminal(terminalId, await customExecution.callback(resolvedDefinition)); } this._lastStartedTask = execution.id; diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index 311a85fa2b2..0715f6affdb 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -11,7 +11,6 @@ import * as types from 'vs/workbench/api/common/extHostTypes'; import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import type * as vscode from 'vscode'; import * as tasks from '../common/shared/tasks'; -import * as Objects from 'vs/base/common/objects'; import { ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostDebugService'; import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; @@ -123,32 +122,6 @@ export class ExtHostTask extends ExtHostTaskBase { return this._variableResolver; } - protected async resolveDefinition(uri: number | UriComponents | undefined, definition: vscode.TaskDefinition | undefined): Promise { - if (!uri || (typeof uri === 'number') || !definition) { - return definition; - } - const workspaceFolder = await this._workspaceProvider.resolveWorkspaceFolder(URI.revive(uri)); - const workspaceFolders = await this._workspaceProvider.getWorkspaceFolders2(); - if (!workspaceFolders || !workspaceFolder) { - return definition; - } - const resolver = await this.getVariableResolver(workspaceFolders); - const ws: IWorkspaceFolder = { - uri: workspaceFolder.uri, - name: workspaceFolder.name, - index: workspaceFolder.index, - toResource: () => { - throw new Error('Not implemented'); - } - }; - const resolvedDefinition = Objects.deepClone(definition); - for (const key in resolvedDefinition) { - resolvedDefinition[key] = resolver.resolve(ws, resolvedDefinition[key]); - } - - return resolvedDefinition; - } - public async $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Promise<{ process?: string, variables: { [key: string]: string; } }> { const uri: URI = URI.revive(uriComponents); const result = { From cc29a814476d9d9366e7687a16cff1af0641fbdf Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Fri, 14 Aug 2020 10:12:47 -0700 Subject: [PATCH 252/736] Update github authentication extension kind --- extensions/github-authentication/package.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index c4189e6b7eb..8136ebd04ca 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -15,6 +15,11 @@ "*", "onAuthenticationRequest:github" ], + "extensionKind": [ + "ui", + "workspace", + "web" + ], "contributes": { "commands": [ { From 33d38a9bfefe6fd4cb4614c0f89faa5a9629e373 Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 14 Aug 2020 16:06:14 -0700 Subject: [PATCH 253/736] re #99151. --- .../notebook/browser/notebook.contribution.ts | 3 +-- .../notebook/browser/notebookBrowser.ts | 18 +++++++++++++++++ .../notebook/browser/notebookEditor.ts | 3 ++- .../notebook/browser/notebookEditorWidget.ts | 20 ++----------------- .../notebook/browser/notebookServiceImpl.ts | 14 ++++++++++--- .../browser/viewModel/notebookViewModel.ts | 10 ++++++++-- .../notebook/test/testNotebookEditor.ts | 6 +++++- 7 files changed, 47 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 7a9953b2be8..e5d962b3ff4 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -37,8 +37,7 @@ import { IEditorService, IOpenEditorOverride } from 'vs/workbench/services/edito import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { CustomEditorsAssociations, customEditorsAssociationsSettingId } from 'vs/workbench/services/editor/common/editorOpenWith'; import { CustomEditorInfo } from 'vs/workbench/contrib/customEditor/common/customEditor'; -import { NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; -import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { INotebookEditor, NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { INotebookEditorModelResolverService, NotebookModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 102858f711e..5b4949cbcbd 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -28,6 +28,8 @@ import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/no import { IMenu } from 'vs/platform/actions/common/actions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { CellLanguageStatusBarItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents'; +import { EditorOptions } from 'vs/workbench/common/editor'; +import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; export const KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED = new RawContextKey('notebookFindWidgetFocused', false); @@ -170,6 +172,21 @@ export interface INotebookDeltaDecoration { options: INotebookCellDecorationOptions; } +export class NotebookEditorOptions extends EditorOptions { + + readonly cellOptions?: IResourceEditorInput; + + constructor(options: Partial) { + super(); + this.overwrite(options); + this.cellOptions = options.cellOptions; + } + + with(options: Partial): NotebookEditorOptions { + return new NotebookEditorOptions({ ...this, ...options }); + } +} + export interface INotebookEditor extends IEditor { cursorNavigationMode: boolean; @@ -209,6 +226,7 @@ export interface INotebookEditor extends IEditor { hasWebviewFocus(): boolean; hasOutputTextSelection(): boolean; + setOptions(options: NotebookEditorOptions | undefined): Promise; /** * Select & focus cell diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index a954e866d9a..f5390824196 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -18,12 +18,13 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { EditorOptions, IEditorInput, IEditorMemento } from 'vs/workbench/common/editor'; import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput'; -import { NotebookEditorOptions, NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; +import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; import { IBorrowValue, INotebookEditorWidgetService } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidgetService'; import { INotebookEditorViewState, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { IEditorDropService } from 'vs/workbench/services/editor/browser/editorDropService'; import { IEditorGroup, IEditorGroupsService, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; const NOTEBOOK_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'NotebookEditorViewState'; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 1f2d6291e32..e0843061a75 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -20,15 +20,14 @@ import { IEditor } from 'vs/editor/common/editorCommon'; import * as nls from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { contrastBorder, editorBackground, focusBorder, foreground, registerColor, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground, textPreformatForeground, errorForeground, transparent, listFocusBackground, listInactiveSelectionBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, scrollbarSliderActiveBackground } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { EditorMemento } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { EditorOptions, IEditorMemento } from 'vs/workbench/common/editor'; +import { IEditorMemento } from 'vs/workbench/common/editor'; import { CELL_MARGIN, CELL_RUN_GUTTER, CELL_TOP_MARGIN, SCROLLABLE_ELEMENT_PADDING_TOP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellEditState, CellFocusMode, ICellRange, ICellViewModel, INotebookCellList, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_OUTPUT_FOCUSED, INotebookDeltaDecoration } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellEditState, CellFocusMode, ICellRange, ICellViewModel, INotebookCellList, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_OUTPUT_FOCUSED, INotebookDeltaDecoration, NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList'; import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer'; @@ -63,21 +62,6 @@ import { CellDragAndDropController } from 'vs/workbench/contrib/notebook/browser const $ = DOM.$; -export class NotebookEditorOptions extends EditorOptions { - - readonly cellOptions?: IResourceEditorInput; - - constructor(options: Partial) { - super(); - this.overwrite(options); - this.cellOptions = options.cellOptions; - } - - with(options: Partial): NotebookEditorOptions { - return new NotebookEditorOptions({ ...this, ...options }); - } -} - const NotebookEditorActiveKernelCache = 'workbench.editor.notebook.activeKernel'; export class NotebookEditorWidget extends Disposable implements INotebookEditor { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index a45e3a397c0..bec8d69a0b3 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -21,7 +21,7 @@ import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/mode import { INotebookService, IMainNotebookController } from 'vs/workbench/contrib/notebook/common/notebookService'; import * as glob from 'vs/base/common/glob'; import { basename } from 'vs/base/common/path'; -import { getActiveNotebookEditor, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { getActiveNotebookEditor, INotebookEditor, NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { Memento } from 'vs/workbench/common/memento'; @@ -342,7 +342,11 @@ export class NotebookService extends Disposable implements INotebookService, ICu this._register(UndoCommand.addImplementation(PRIORITY, () => { const { editor } = getContext(); if (editor?.viewModel) { - editor?.viewModel.undo(); + editor?.viewModel.undo().then(cellResources => { + if (cellResources?.length) { + editor?.setOptions(new NotebookEditorOptions({ cellOptions: { resource: cellResources[0] } })); + } + }); return true; } @@ -352,7 +356,11 @@ export class NotebookService extends Disposable implements INotebookService, ICu this._register(RedoCommand.addImplementation(PRIORITY, () => { const { editor } = getContext(); if (editor?.viewModel) { - editor?.viewModel.redo(); + editor?.viewModel.redo().then(cellResources => { + if (cellResources?.length) { + editor?.setOptions(new NotebookEditorOptions({ cellOptions: { resource: cellResources[0] } })); + } + }); return true; } diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts index 2f2f79f1746..b04c3e18ed5 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts @@ -1076,12 +1076,15 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD const element = editStack.past.length ? editStack.past[editStack.past.length - 1] : undefined; if (element && element instanceof SingleModelEditStackElement || element instanceof MultiModelEditStackElement) { - return await this.withElement(element, async () => { + await this.withElement(element, async () => { await this._undoService.undo(this.uri); }); + + return (element instanceof SingleModelEditStackElement) ? [element.resource] : element.resources; } await this._undoService.undo(this.uri); + return []; } async redo() { @@ -1093,13 +1096,16 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD const element = editStack.future[0]; if (element && element instanceof SingleModelEditStackElement || element instanceof MultiModelEditStackElement) { - return await this.withElement(element, async () => { + await this.withElement(element, async () => { await this._undoService.redo(this.uri); }); + + return (element instanceof SingleModelEditStackElement) ? [element.resource] : element.resources; } await this._undoService.redo(this.uri); + return []; } equal(notebook: NotebookTextModel) { diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index 7f43e90969f..463c36e60c3 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -12,7 +12,7 @@ import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { Range } from 'vs/editor/common/core/range'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { EditorModel } from 'vs/workbench/common/editor'; -import { ICellRange, ICellViewModel, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { ICellRange, ICellViewModel, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo, NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer'; import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { CellViewModel, IModelDecorationsChangeAccessor, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; @@ -63,6 +63,10 @@ export class TestNotebookEditor implements INotebookEditor { constructor( ) { } + setOptions(options: NotebookEditorOptions | undefined): Promise { + throw new Error('Method not implemented.'); + } + hideInset(output: IProcessedOutput): void { throw new Error('Method not implemented.'); } From e0cc8f579f6b911a7eb72dea231ad89dace369b8 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 14 Aug 2020 17:29:01 -0700 Subject: [PATCH 254/736] Queue replace actions within a filematch because other matches in the file should be considered stale after one replace has been executed Fix #88282 --- .../contrib/search/browser/searchActions.ts | 36 +++++++++---------- .../contrib/search/common/searchModel.ts | 9 +++-- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index 01d1831bb0a..c5eedd23793 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -683,6 +683,8 @@ export class ReplaceAction extends AbstractSearchAndReplaceAction { static readonly LABEL = nls.localize('match.replace.label', "Replace"); + static runQ = Promise.resolve(); + constructor(private viewer: WorkbenchObjectTree, private element: Match, private viewlet: SearchView, @IReplaceService private readonly replaceService: IReplaceService, @IKeybindingService keyBindingService: IKeybindingService, @@ -691,26 +693,24 @@ export class ReplaceAction extends AbstractSearchAndReplaceAction { super(Constants.ReplaceActionId, appendKeyBindingLabel(ReplaceAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceActionId), keyBindingService), searchReplaceIcon.classNames); } - run(): Promise { + async run(): Promise { this.enabled = false; - return this.element.parent().replace(this.element).then(() => { - const elementToFocus = this.getElementToFocusAfterReplace(); - if (elementToFocus) { - this.viewer.setFocus([elementToFocus], getSelectionKeyboardEvent()); - } + await this.element.parent().replace(this.element); + const elementToFocus = this.getElementToFocusAfterReplace(); + if (elementToFocus) { + this.viewer.setFocus([elementToFocus], getSelectionKeyboardEvent()); + } - return this.getElementToShowReplacePreview(elementToFocus); - }).then(elementToShowReplacePreview => { - this.viewer.domFocus(); + const elementToShowReplacePreview = this.getElementToShowReplacePreview(elementToFocus); + this.viewer.domFocus(); - const useReplacePreview = this.configurationService.getValue().search.useReplacePreview; - if (!useReplacePreview || !elementToShowReplacePreview || this.hasToOpenFile()) { - this.viewlet.open(this.element, true); - } else { - this.replaceService.openReplacePreview(elementToShowReplacePreview, true); - } - }); + const useReplacePreview = this.configurationService.getValue().search.useReplacePreview; + if (!useReplacePreview || !elementToShowReplacePreview || this.hasToOpenFile()) { + this.viewlet.open(this.element, true); + } else { + this.replaceService.openReplacePreview(elementToShowReplacePreview, true); + } } private getElementToFocusAfterReplace(): RenderableMatch { @@ -740,11 +740,11 @@ export class ReplaceAction extends AbstractSearchAndReplaceAction { return elementToFocus!; } - private async getElementToShowReplacePreview(elementToFocus: RenderableMatch): Promise { + private getElementToShowReplacePreview(elementToFocus: RenderableMatch): Match | null { if (this.hasSameParent(elementToFocus)) { return elementToFocus; } - const previousElement = await this.getPreviousElementAfterRemoved(this.viewer, this.element); + const previousElement = this.getPreviousElementAfterRemoved(this.viewer, this.element); if (this.hasSameParent(previousElement)) { return previousElement; } diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index e5bbd712833..983355a2396 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -360,9 +360,12 @@ export class FileMatch extends Disposable implements IFileMatch { this._onChange.fire({ didRemove: true }); } - replace(toReplace: Match): Promise { - return this.replaceService.replace(toReplace) - .then(() => this.updatesMatchesForLineAfterReplace(toReplace.range().startLineNumber, false)); + private replaceQ = Promise.resolve(); + async replace(toReplace: Match): Promise { + return this.replaceQ = this.replaceQ.finally(async () => { + await this.replaceService.replace(toReplace); + this.updatesMatchesForLineAfterReplace(toReplace.range().startLineNumber, false); + }); } setSelectedMatch(match: Match | null): void { From 76b6ad9981c115ff3e5bb52c95a38d2e9ec7ba12 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 13 Aug 2020 23:28:43 -0700 Subject: [PATCH 255/736] Make sure we always dispose of all disposed of entries even if one of them throws --- src/vs/base/common/lifecycle.ts | 32 +++++++- src/vs/base/test/common/lifecycle.test.ts | 89 ++++++++++++++++++++++- 2 files changed, 116 insertions(+), 5 deletions(-) diff --git a/src/vs/base/common/lifecycle.ts b/src/vs/base/common/lifecycle.ts index 406a9c801d4..79bbb910aac 100644 --- a/src/vs/base/common/lifecycle.ts +++ b/src/vs/base/common/lifecycle.ts @@ -45,6 +45,14 @@ function trackDisposable(x: T): T { return x; } +export class MultiDisposeError extends Error { + constructor( + public readonly errors: any[] + ) { + super(`Encounter errors while disposing of store. Errors: [${errors.join(', ')}]`); + } +} + export interface IDisposable { dispose(): void; } @@ -60,12 +68,25 @@ export function dispose(disposables: Array): Array; export function dispose(disposables: ReadonlyArray): ReadonlyArray; export function dispose(arg: T | IterableIterator | undefined): any { if (Iterable.is(arg)) { - for (let d of arg) { + let errors: any[] = []; + + for (const d of arg) { if (d) { markTracked(d); - d.dispose(); + try { + d.dispose(); + } catch (e) { + errors.push(e); + } } } + + if (errors.length === 1) { + throw errors[0]; + } else if (errors.length > 1) { + throw new MultiDisposeError(errors); + } + return Array.isArray(arg) ? [] : arg; } else if (arg) { markTracked(arg); @@ -116,8 +137,11 @@ export class DisposableStore implements IDisposable { * Dispose of all registered disposables but do not mark this object as disposed. */ public clear(): void { - this._toDispose.forEach(item => item.dispose()); - this._toDispose.clear(); + try { + dispose(this._toDispose.values()); + } finally { + this._toDispose.clear(); + } } public add(t: T): T { diff --git a/src/vs/base/test/common/lifecycle.test.ts b/src/vs/base/test/common/lifecycle.test.ts index 91f17aedb44..7aa87cc6b0f 100644 --- a/src/vs/base/test/common/lifecycle.test.ts +++ b/src/vs/base/test/common/lifecycle.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 { IDisposable, dispose, ReferenceCollection } from 'vs/base/common/lifecycle'; +import { DisposableStore, dispose, IDisposable, MultiDisposeError, ReferenceCollection, toDisposable } from 'vs/base/common/lifecycle'; class Disposable implements IDisposable { isDisposed = false; @@ -49,6 +49,48 @@ suite('Lifecycle', () => { assert(disposable2.isDisposed); }); + test('dispose array should dispose all if a child throws on dispose', () => { + const disposedValues = new Set(); + + let thrownError: any; + try { + dispose([ + toDisposable(() => { disposedValues.add(1); }), + toDisposable(() => { throw new Error('I am error'); }), + toDisposable(() => { disposedValues.add(3); }), + ]); + } catch (e) { + thrownError = e; + } + + assert.ok(disposedValues.has(1)); + assert.ok(disposedValues.has(3)); + assert.strictEqual(thrownError.message, 'I am error'); + }); + + test('dispose array should rethrow composite error if multiple entries throw on dispose', () => { + const disposedValues = new Set(); + + let thrownError: any; + try { + dispose([ + toDisposable(() => { disposedValues.add(1); }), + toDisposable(() => { throw new Error('I am error 1'); }), + toDisposable(() => { throw new Error('I am error 2'); }), + toDisposable(() => { disposedValues.add(4); }), + ]); + } catch (e) { + thrownError = e; + } + + assert.ok(disposedValues.has(1)); + assert.ok(disposedValues.has(4)); + assert.ok(thrownError instanceof MultiDisposeError); + assert.strictEqual((thrownError as MultiDisposeError).errors.length, 2); + assert.strictEqual((thrownError as MultiDisposeError).errors[0].message, 'I am error 1'); + assert.strictEqual((thrownError as MultiDisposeError).errors[1].message, 'I am error 2'); + }); + test('Action bar has broken accessibility #100273', function () { let array = [{ dispose() { } }, { dispose() { } }]; let array2 = dispose(array); @@ -61,7 +103,52 @@ suite('Lifecycle', () => { let setValues = set.values(); let setValues2 = dispose(setValues); assert.ok(setValues === setValues2); + }); +}); +suite('DisposableStore', () => { + test('dispose should call all child disposes even if a child throws on dispose', () => { + const disposedValues = new Set(); + + const store = new DisposableStore(); + store.add(toDisposable(() => { disposedValues.add(1); })); + store.add(toDisposable(() => { throw new Error('I am error'); })); + store.add(toDisposable(() => { disposedValues.add(3); })); + + let thrownError: any; + try { + store.dispose(); + } catch (e) { + thrownError = e; + } + + assert.ok(disposedValues.has(1)); + assert.ok(disposedValues.has(3)); + assert.strictEqual(thrownError.message, 'I am error'); + }); + + test('dispose should throw composite error if multiple children throw on dispose', () => { + const disposedValues = new Set(); + + const store = new DisposableStore(); + store.add(toDisposable(() => { disposedValues.add(1); })); + store.add(toDisposable(() => { throw new Error('I am error 1'); })); + store.add(toDisposable(() => { throw new Error('I am error 2'); })); + store.add(toDisposable(() => { disposedValues.add(4); })); + + let thrownError: any; + try { + store.dispose(); + } catch (e) { + thrownError = e; + } + + assert.ok(disposedValues.has(1)); + assert.ok(disposedValues.has(4)); + assert.ok(thrownError instanceof MultiDisposeError); + assert.strictEqual((thrownError as MultiDisposeError).errors.length, 2); + assert.strictEqual((thrownError as MultiDisposeError).errors[0].message, 'I am error 1'); + assert.strictEqual((thrownError as MultiDisposeError).errors[1].message, 'I am error 2'); }); }); From 0a22f70e66bee825459d1f25539395094be0b6bc Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 14 Aug 2020 21:59:40 -0700 Subject: [PATCH 256/736] Try disabling the polling based loading for serverless --- src/vs/workbench/contrib/webview/browser/pre/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index 630d5d93422..8cd6211afde 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -514,7 +514,7 @@ }, 0); } - if (host.fakeLoad) { + if (host.fakeLoad && false) { // On Safari for iframes with scripts disabled, the `DOMContentLoaded` never seems to be fired. // Use polling instead. const interval = setInterval(() => { From 05b8ef8947cd3fb2565310999392d6cdc799dc0f Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Sun, 16 Aug 2020 01:34:05 -0500 Subject: [PATCH 257/736] Fixes #104641 --- src/vs/workbench/contrib/files/browser/views/explorerView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 66d94162794..c35e3fa820b 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -66,7 +66,7 @@ function hasExpandedRootChild(tree: WorkbenchCompressibleAsyncDataTree Date: Thu, 16 Jul 2020 15:16:55 +0100 Subject: [PATCH 258/736] Implemented filter for debug console output --- .../debug/browser/media/replFilter.css | 41 ++++ .../workbench/contrib/debug/browser/repl.ts | 35 ++++ .../contrib/debug/browser/replFilter.ts | 66 +++++++ .../contrib/debug/browser/replFilterView.ts | 180 ++++++++++++++++++ 4 files changed, 322 insertions(+) create mode 100644 src/vs/workbench/contrib/debug/browser/media/replFilter.css create mode 100644 src/vs/workbench/contrib/debug/browser/replFilter.ts create mode 100644 src/vs/workbench/contrib/debug/browser/replFilterView.ts diff --git a/src/vs/workbench/contrib/debug/browser/media/replFilter.css b/src/vs/workbench/contrib/debug/browser/media/replFilter.css new file mode 100644 index 00000000000..7d165e7af56 --- /dev/null +++ b/src/vs/workbench/contrib/debug/browser/media/replFilter.css @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-action-bar .action-item.repl-panel-action-filter-container { + cursor: default; + display: flex; +} + +.monaco-action-bar .repl-panel-action-filter{ + display: flex; + align-items: center; + flex: 1; +} + +.monaco-action-bar .repl-panel-action-filter .monaco-inputbox { + height: 24px; + font-size: 12px; + flex: 1; +} + +.pane-header .monaco-action-bar .repl-panel-action-filter .monaco-inputbox { + height: 20px; + line-height: 18px; +} + +.monaco-workbench.vs .monaco-action-bar .repl-panel-action-filter .monaco-inputbox { + height: 25px; +} + +.panel > .title .monaco-action-bar .action-item.repl-panel-action-filter-container { + max-width: 600px; + min-width: 300px; + margin-right: 10px; +} + +.repl-tree .monaco-action-bar .action-item.repl-panel-action-filter-container, +.panel > .title .monaco-action-bar .action-item.repl-panel-action-filter-container.grow { + flex: 1; +} diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index a5e01950a9a..ac43245f3f6 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -59,10 +59,14 @@ import { ReplGroup } from 'vs/workbench/contrib/debug/common/replModel'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { EDITOR_FONT_DEFAULTS, EditorOption } from 'vs/editor/common/config/editorOptions'; import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; +import { ReplFilterActionViewItem, ReplFilterState } from 'vs/workbench/contrib/debug/browser/replFilterView'; +import { Memento, MementoObject } from 'vs/workbench/common/memento'; +import { ReplFilter } from 'vs/workbench/contrib/debug/browser/replFilter'; const $ = dom.$; const HISTORY_STORAGE_KEY = 'debug.repl.history'; +const FILTER_STATE_STORAGE_KEY = 'debug.repl.filter'; const DECORATION_KEY = 'replinputdecoration'; @@ -93,6 +97,10 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { private styleElement: HTMLStyleElement | undefined; private completionItemProvider: IDisposable | undefined; private modelChangeListener: IDisposable = Disposable.None; + private readonly panelState: MementoObject; + private readonly filterActionId: string = `workbench.actions.treeView.${this.id}.filter`; + private readonly filterState: ReplFilterState; + private readonly filter: ReplFilter; constructor( options: IViewPaneOptions, @@ -116,6 +124,16 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this.history = new HistoryNavigator(JSON.parse(this.storageService.get(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE, '[]')), 50); + this.panelState = new Memento(FILTER_STATE_STORAGE_KEY, storageService).getMemento(StorageScope.WORKSPACE); + + const initialFilterText = this.panelState['filter'] || ''; + this.filter = new ReplFilter(initialFilterText); + this.filterState = this._register(new ReplFilterState({ + filterText: initialFilterText, + filterHistory: this.panelState['filterHistory'] || [], + layout: new dom.Dimension(0, 0), + })); + codeEditorService.registerDecorationType(DECORATION_KEY, {}); this.registerListeners(); } @@ -237,6 +255,15 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { this._register(this.editorService.onDidActiveEditorChange(() => { this.setMode(); })); + + this._register(this.filterState.onDidChange((e) => { + if (e.filterText) { + this.filter.filterQuery = this.filterState.filterText; + if (this.tree) { + this.tree.refilter(); + } + } + })); } get isReadonly(): boolean { @@ -428,6 +455,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { this.replInputContainer.style.height = `${replInputHeight}px`; this.replInput.layout({ width: width - 30, height: replInputHeight }); + this.filterState.layout = new dom.Dimension(width, height); } focus(): void { @@ -437,6 +465,8 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { getActionViewItem(action: IAction): IActionViewItem | undefined { if (action.id === SelectReplAction.ID) { return this.instantiationService.createInstance(SelectReplActionViewItem, this.selectReplAction); + } else if (action.id === this.filterActionId) { + return this.instantiationService.createInstance(ReplFilterActionViewItem, action, this.filterState); } return super.getActionViewItem(action); @@ -444,6 +474,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { getActions(): IAction[] { const result: IAction[] = []; + result.push(new Action(this.filterActionId)); if (this.debugService.getModel().getSessions(true).filter(s => s.hasSeparateRepl() && !sessionsToIgnore.has(s)).length > 1) { result.push(this.selectReplAction); } @@ -532,6 +563,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { // https://github.com/microsoft/TypeScript/issues/32526 new ReplDataSource() as IAsyncDataSource, { + filter: this.filter, accessibilityProvider: new ReplAccessibilityProvider(), identityProvider: { getId: (element: IReplElement) => element.getId() }, mouseSupport: false, @@ -682,6 +714,9 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { this.storageService.remove(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE); } + this.panelState['filter'] = this.filterState.filterText; + this.panelState['filterHistory'] = this.filterState.filterHistory; + super.saveState(); } diff --git a/src/vs/workbench/contrib/debug/browser/replFilter.ts b/src/vs/workbench/contrib/debug/browser/replFilter.ts new file mode 100644 index 00000000000..568051d9f7c --- /dev/null +++ b/src/vs/workbench/contrib/debug/browser/replFilter.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { matchesFuzzy } from 'vs/base/common/filters'; +import { splitGlobAware } from 'vs/base/common/glob'; +import * as strings from 'vs/base/common/strings'; +import { ITreeFilter, TreeVisibility, TreeFilterResult } from 'vs/base/browser/ui/tree/tree'; +import { IReplElement } from 'vs/workbench/contrib/debug/common/debug'; + +type ParsedQuery = { + type: 'include' | 'exclude', + query: string, +}; + +export class ReplFilter implements ITreeFilter { + + static matchQuery = matchesFuzzy; + + constructor(initialQuery: string) { + this.filterQuery = initialQuery; + } + + private _parsedQueries: ParsedQuery[] = []; + set filterQuery(query: string) { + this._parsedQueries = []; + query = query.trim(); + + if (query && query !== '') { + const filters = splitGlobAware(query, ',').map(s => s.trim()).filter(s => !!s.length); + for (const f of filters) { + if (strings.startsWith(f, '!')) { + this._parsedQueries.push({ type: 'exclude', query: f.slice(1) }); + } else { + this._parsedQueries.push({ type: 'include', query: f }); + } + } + } + } + + filter(element: IReplElement, parentVisibility: TreeVisibility): TreeFilterResult { + if (this._parsedQueries.length === 0) { + return parentVisibility; + } + + let includeQueryPresent = false; + let includeQueryMatched = false; + + const text = element.toString(); + + for (let { type, query } of this._parsedQueries) { + if (type === 'exclude' && ReplFilter.matchQuery(query, text)) { + // If exclude query matches, ignore all other queries and hide + return false; + } else if (type === 'include') { + includeQueryPresent = true; + if (ReplFilter.matchQuery(query, text)) { + includeQueryMatched = true; + } + } + } + + return includeQueryPresent ? includeQueryMatched : parentVisibility; + } +} diff --git a/src/vs/workbench/contrib/debug/browser/replFilterView.ts b/src/vs/workbench/contrib/debug/browser/replFilterView.ts new file mode 100644 index 00000000000..b6156b1507b --- /dev/null +++ b/src/vs/workbench/contrib/debug/browser/replFilterView.ts @@ -0,0 +1,180 @@ +/*--------------------------------------------------------------------------------------------- + * 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/replFilter'; +import * as DOM from 'vs/base/browser/dom'; +import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Delayer } from 'vs/base/common/async'; +import { IAction } from 'vs/base/common/actions'; +import { HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { toDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { Event, Emitter } from 'vs/base/common/event'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { ContextScopedHistoryInputBox } from 'vs/platform/browser/contextScopedHistoryWidget'; +import { localize } from 'vs/nls'; + +export interface IReplFiltersChangeEvent { + filterText?: boolean; + layout?: boolean; +} + +export interface IReplFiltersOptions { + filterText: string; + filterHistory: string[]; + layout: DOM.Dimension; +} + +export class ReplFilterState extends Disposable { + + private readonly _onDidChange: Emitter = this._register(new Emitter()); + readonly onDidChange: Event = this._onDidChange.event; + + constructor(options: IReplFiltersOptions) { + super(); + this._filterText = options.filterText; + this.filterHistory = options.filterHistory; + this._layout = options.layout; + } + + private _filterText: string; + get filterText(): string { + return this._filterText; + } + set filterText(filterText: string) { + if (this._filterText !== filterText) { + this._filterText = filterText; + this._onDidChange.fire({ filterText: true }); + } + } + + filterHistory: string[]; + + private _layout: DOM.Dimension = new DOM.Dimension(0, 0); + get layout(): DOM.Dimension { + return this._layout; + } + set layout(layout: DOM.Dimension) { + if (this._layout.width !== layout.width || this._layout.height !== layout.height) { + this._layout = layout; + this._onDidChange.fire({ layout: true }); + } + } +} + +export class ReplFilterActionViewItem extends BaseActionViewItem { + + private delayedFilterUpdate: Delayer; + private container: HTMLElement | undefined; + private filterInputBox: HistoryInputBox | undefined; + + constructor( + action: IAction, + private filters: ReplFilterState, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IContextViewService private readonly contextViewService: IContextViewService) { + super(null, action); + this.delayedFilterUpdate = new Delayer(200); + this._register(toDisposable(() => this.delayedFilterUpdate.cancel())); + } + + render(container: HTMLElement): void { + this.container = container; + DOM.addClass(this.container, 'repl-panel-action-filter-container'); + + this.element = DOM.append(this.container, DOM.$('')); + this.element.className = this.class; + this.createInput(this.element); + this.updateClass(); + } + + focus(): void { + if (this.filterInputBox) { + this.filterInputBox.focus(); + } + } + + private clearFilterText(): void { + if (this.filterInputBox) { + this.filterInputBox.value = ''; + } + } + + private createInput(container: HTMLElement): void { + this.filterInputBox = this._register(this.instantiationService.createInstance(ContextScopedHistoryInputBox, container, this.contextViewService, { + placeholder: localize('workbench.debug.filter.placeholder', "Filter. E.g.: text, !exclude"), + history: this.filters.filterHistory + })); + this.filterInputBox.value = this.filters.filterText; + this._register(this.filterInputBox.onDidChange(() => this.delayedFilterUpdate.trigger(() => this.onDidInputChange(this.filterInputBox!)))); + this._register(this.filters.onDidChange((event: IReplFiltersChangeEvent) => { + if (event.filterText) { + this.filterInputBox!.value = this.filters.filterText; + } + })); + this._register(DOM.addStandardDisposableListener(this.filterInputBox.inputElement, DOM.EventType.KEY_DOWN, (e: any) => this.onInputKeyDown(e))); + this._register(DOM.addStandardDisposableListener(container, DOM.EventType.KEY_DOWN, this.handleKeyboardEvent)); + this._register(DOM.addStandardDisposableListener(container, DOM.EventType.KEY_UP, this.handleKeyboardEvent)); + this._register(DOM.addStandardDisposableListener(this.filterInputBox.inputElement, DOM.EventType.CLICK, (e) => { + e.stopPropagation(); + e.preventDefault(); + })); + this._register(this.filters.onDidChange(e => this.onDidFiltersChange(e))); + } + + private onDidFiltersChange(e: IReplFiltersChangeEvent): void { + if (e.layout) { + this.updateClass(); + } + } + + private onDidInputChange(inputbox: HistoryInputBox) { + inputbox.addToHistory(); + this.filters.filterText = inputbox.value; + this.filters.filterHistory = inputbox.getHistory(); + } + + // Action toolbar is swallowing some keys for action items which should not be for an input box + private handleKeyboardEvent(event: StandardKeyboardEvent) { + if (event.equals(KeyCode.Space) + || event.equals(KeyCode.LeftArrow) + || event.equals(KeyCode.RightArrow) + || event.equals(KeyCode.Escape) + ) { + event.stopPropagation(); + } + } + + private onInputKeyDown(event: StandardKeyboardEvent) { + let handled = false; + if (event.equals(KeyCode.Escape)) { + this.clearFilterText(); + handled = true; + } + if (handled) { + event.stopPropagation(); + event.preventDefault(); + } + } + + protected updateClass(): void { + if (this.element && this.container) { + this.element.className = this.class; + DOM.toggleClass(this.container, 'grow', DOM.hasClass(this.element, 'grow')); + } + } + + protected get class(): string { + if (this.filters.layout.width > 800) { + return 'repl-panel-action-filter grow'; + } else if (this.filters.layout.width < 600) { + return 'repl-panel-action-filter small'; + } else { + return 'repl-panel-action-filter'; + } + } +} From 166da46467dcd9e79b9f7be5b42ca63714fddd70 Mon Sep 17 00:00:00 2001 From: Arthur Kushka Date: Thu, 30 Jul 2020 18:46:51 +0100 Subject: [PATCH 259/736] Fixed rebase issues, added test for filtering --- .../contrib/debug/browser/replFilterView.ts | 2 +- .../contrib/debug/common/replModel.ts | 11 +++- .../contrib/debug/test/browser/repl.test.ts | 57 +++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/replFilterView.ts b/src/vs/workbench/contrib/debug/browser/replFilterView.ts index b6156b1507b..a0b73db76e5 100644 --- a/src/vs/workbench/contrib/debug/browser/replFilterView.ts +++ b/src/vs/workbench/contrib/debug/browser/replFilterView.ts @@ -5,7 +5,7 @@ import 'vs/css!./media/replFilter'; import * as DOM from 'vs/base/browser/dom'; -import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { Delayer } from 'vs/base/common/async'; import { IAction } from 'vs/base/common/actions'; import { HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox'; diff --git a/src/vs/workbench/contrib/debug/common/replModel.ts b/src/vs/workbench/contrib/debug/common/replModel.ts index 87d3348b97c..b0aa0128449 100644 --- a/src/vs/workbench/contrib/debug/common/replModel.ts +++ b/src/vs/workbench/contrib/debug/common/replModel.ts @@ -174,13 +174,18 @@ export class ReplGroup implements IReplElement { } } +type FilterFunc = ((element: IReplElement) => void); + export class ReplModel { private replElements: IReplElement[] = []; private readonly _onDidChangeElements = new Emitter(); readonly onDidChangeElements = this._onDidChangeElements.event; + private filterFunc: FilterFunc | undefined; getReplElements(): IReplElement[] { - return this.replElements; + return this.replElements.filter(element => + this.filterFunc ? this.filterFunc(element) : true + ); } async addReplExpression(session: IDebugSession, stackFrame: IStackFrame | undefined, name: string): Promise { @@ -315,6 +320,10 @@ export class ReplModel { } } + setFilter(filterFunc: FilterFunc): void { + this.filterFunc = filterFunc; + } + removeReplExpressions(): void { if (this.replElements.length > 0) { this.replElements = []; diff --git a/src/vs/workbench/contrib/debug/test/browser/repl.test.ts b/src/vs/workbench/contrib/debug/test/browser/repl.test.ts index a8c0c42173e..470c435015e 100644 --- a/src/vs/workbench/contrib/debug/test/browser/repl.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/repl.test.ts @@ -12,6 +12,8 @@ import { SimpleReplElement, RawObjectReplElement, ReplEvaluationInput, ReplModel import { RawDebugSession } from 'vs/workbench/contrib/debug/browser/rawDebugSession'; import { timeout } from 'vs/base/common/async'; import { createMockSession } from 'vs/workbench/contrib/debug/test/browser/callStack.test'; +import { ReplFilter } from 'vs/workbench/contrib/debug/browser/replFilter'; +import { TreeVisibility } from 'vs/base/browser/ui/tree/tree'; suite('Debug - REPL', () => { let model: DebugModel; @@ -189,4 +191,59 @@ suite('Debug - REPL', () => { assert.equal(repl.getReplElements().length, 3); assert.equal((repl.getReplElements()[2]).value, 'second global line'); }); + + test('repl filter', async () => { + const session = createMockSession(model); + const repl = new ReplModel(); + const replFilter = new ReplFilter(''); + + repl.setFilter((element) => { + const filterResult = replFilter.filter(element, TreeVisibility.Visible); + return filterResult === true || filterResult === TreeVisibility.Visible; + }); + + repl.appendToRepl(session, 'first line\n', severity.Info); + repl.appendToRepl(session, 'second line\n', severity.Info); + repl.appendToRepl(session, 'third line\n', severity.Info); + repl.appendToRepl(session, 'fourth line\n', severity.Info); + + replFilter.filterQuery = 'first'; + let r1 = repl.getReplElements(); + assert.equal(r1.length, 1); + assert.equal(r1[0].value, 'first line\n'); + + replFilter.filterQuery = '!first'; + let r2 = repl.getReplElements(); + assert.equal(r1.length, 1); + assert.equal(r2[0].value, 'second line\n'); + assert.equal(r2[1].value, 'third line\n'); + assert.equal(r2[2].value, 'fourth line\n'); + + replFilter.filterQuery = 'first, line'; + let r3 = repl.getReplElements(); + assert.equal(r3.length, 4); + assert.equal(r3[0].value, 'first line\n'); + assert.equal(r3[1].value, 'second line\n'); + assert.equal(r3[2].value, 'third line\n'); + assert.equal(r3[3].value, 'fourth line\n'); + + replFilter.filterQuery = 'line, !second'; + let r4 = repl.getReplElements(); + assert.equal(r4.length, 3); + assert.equal(r4[0].value, 'first line\n'); + assert.equal(r4[1].value, 'third line\n'); + assert.equal(r4[2].value, 'fourth line\n'); + + replFilter.filterQuery = '!second, line'; + let r4_same = repl.getReplElements(); + assert.equal(r4.length, r4_same.length); + + replFilter.filterQuery = '!line'; + let r5 = repl.getReplElements(); + assert.equal(r5.length, 0); + + replFilter.filterQuery = 'smth'; + let r6 = repl.getReplElements(); + assert.equal(r6.length, 0); + }); }); From bbbc3141cf4c3b2c590f4b6fcfb3f50dc041e011 Mon Sep 17 00:00:00 2001 From: Arthur Kushka Date: Sun, 16 Aug 2020 12:56:06 +0100 Subject: [PATCH 260/736] Moved repl tree filter to the separate common component --- .../workbench/contrib/debug/browser/repl.ts | 32 ++++++---------- .../contrib/debug/browser/replFilter.ts | 4 -- .../contrib/debug/test/browser/repl.test.ts | 2 +- .../browser/media/treeFilter.css} | 16 ++++---- .../browser/treeFilterView.ts} | 37 ++++++++++++------- 5 files changed, 43 insertions(+), 48 deletions(-) rename src/vs/workbench/contrib/{debug/browser/media/replFilter.css => treeFilter/browser/media/treeFilter.css} (54%) rename src/vs/workbench/contrib/{debug/browser/replFilterView.ts => treeFilter/browser/treeFilterView.ts} (85%) diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index ac43245f3f6..5ec4fa0aa67 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -59,16 +59,14 @@ import { ReplGroup } from 'vs/workbench/contrib/debug/common/replModel'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { EDITOR_FONT_DEFAULTS, EditorOption } from 'vs/editor/common/config/editorOptions'; import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; -import { ReplFilterActionViewItem, ReplFilterState } from 'vs/workbench/contrib/debug/browser/replFilterView'; -import { Memento, MementoObject } from 'vs/workbench/common/memento'; +import { TreeFilterPanelActionViewItem, TreeFilterState } from 'vs/workbench/contrib/treeFilter/browser/treeFilterView'; import { ReplFilter } from 'vs/workbench/contrib/debug/browser/replFilter'; const $ = dom.$; const HISTORY_STORAGE_KEY = 'debug.repl.history'; -const FILTER_STATE_STORAGE_KEY = 'debug.repl.filter'; const DECORATION_KEY = 'replinputdecoration'; - +const FILTER_ACTION_ID = `workbench.actions.treeView.repl.filter`; function revealLastElement(tree: WorkbenchAsyncDataTree) { tree.scrollTop = tree.scrollHeight - tree.renderHeight; @@ -97,10 +95,8 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { private styleElement: HTMLStyleElement | undefined; private completionItemProvider: IDisposable | undefined; private modelChangeListener: IDisposable = Disposable.None; - private readonly panelState: MementoObject; - private readonly filterActionId: string = `workbench.actions.treeView.${this.id}.filter`; - private readonly filterState: ReplFilterState; - private readonly filter: ReplFilter; + private filter: ReplFilter; + private filterState: TreeFilterState; constructor( options: IViewPaneOptions, @@ -124,13 +120,10 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this.history = new HistoryNavigator(JSON.parse(this.storageService.get(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE, '[]')), 50); - this.panelState = new Memento(FILTER_STATE_STORAGE_KEY, storageService).getMemento(StorageScope.WORKSPACE); - - const initialFilterText = this.panelState['filter'] || ''; - this.filter = new ReplFilter(initialFilterText); - this.filterState = this._register(new ReplFilterState({ - filterText: initialFilterText, - filterHistory: this.panelState['filterHistory'] || [], + this.filter = new ReplFilter(); + this.filterState = this._register(new TreeFilterState({ + filterText: '', + filterHistory: [], layout: new dom.Dimension(0, 0), })); @@ -465,8 +458,8 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { getActionViewItem(action: IAction): IActionViewItem | undefined { if (action.id === SelectReplAction.ID) { return this.instantiationService.createInstance(SelectReplActionViewItem, this.selectReplAction); - } else if (action.id === this.filterActionId) { - return this.instantiationService.createInstance(ReplFilterActionViewItem, action, this.filterState); + } else if (action.id === FILTER_ACTION_ID) { + return this.instantiationService.createInstance(TreeFilterPanelActionViewItem, action, localize('workbench.debug.filter.placeholder', "Filter. E.g.: text, !exclude"), this.filterState); } return super.getActionViewItem(action); @@ -474,7 +467,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { getActions(): IAction[] { const result: IAction[] = []; - result.push(new Action(this.filterActionId)); + result.push(new Action(FILTER_ACTION_ID)); if (this.debugService.getModel().getSessions(true).filter(s => s.hasSeparateRepl() && !sessionsToIgnore.has(s)).length > 1) { result.push(this.selectReplAction); } @@ -714,9 +707,6 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { this.storageService.remove(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE); } - this.panelState['filter'] = this.filterState.filterText; - this.panelState['filterHistory'] = this.filterState.filterHistory; - super.saveState(); } diff --git a/src/vs/workbench/contrib/debug/browser/replFilter.ts b/src/vs/workbench/contrib/debug/browser/replFilter.ts index 568051d9f7c..243c10f8d6a 100644 --- a/src/vs/workbench/contrib/debug/browser/replFilter.ts +++ b/src/vs/workbench/contrib/debug/browser/replFilter.ts @@ -18,10 +18,6 @@ export class ReplFilter implements ITreeFilter { static matchQuery = matchesFuzzy; - constructor(initialQuery: string) { - this.filterQuery = initialQuery; - } - private _parsedQueries: ParsedQuery[] = []; set filterQuery(query: string) { this._parsedQueries = []; diff --git a/src/vs/workbench/contrib/debug/test/browser/repl.test.ts b/src/vs/workbench/contrib/debug/test/browser/repl.test.ts index 470c435015e..7440c4df8b8 100644 --- a/src/vs/workbench/contrib/debug/test/browser/repl.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/repl.test.ts @@ -195,7 +195,7 @@ suite('Debug - REPL', () => { test('repl filter', async () => { const session = createMockSession(model); const repl = new ReplModel(); - const replFilter = new ReplFilter(''); + const replFilter = new ReplFilter(); repl.setFilter((element) => { const filterResult = replFilter.filter(element, TreeVisibility.Visible); diff --git a/src/vs/workbench/contrib/debug/browser/media/replFilter.css b/src/vs/workbench/contrib/treeFilter/browser/media/treeFilter.css similarity index 54% rename from src/vs/workbench/contrib/debug/browser/media/replFilter.css rename to src/vs/workbench/contrib/treeFilter/browser/media/treeFilter.css index 7d165e7af56..30c22462797 100644 --- a/src/vs/workbench/contrib/debug/browser/media/replFilter.css +++ b/src/vs/workbench/contrib/treeFilter/browser/media/treeFilter.css @@ -3,39 +3,39 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-action-bar .action-item.repl-panel-action-filter-container { +.monaco-action-bar .action-item.panel-action-tree-filter-container { cursor: default; display: flex; } -.monaco-action-bar .repl-panel-action-filter{ +.monaco-action-bar .panel-action-tree-filter{ display: flex; align-items: center; flex: 1; } -.monaco-action-bar .repl-panel-action-filter .monaco-inputbox { +.monaco-action-bar .panel-action-tree-filter .monaco-inputbox { height: 24px; font-size: 12px; flex: 1; } -.pane-header .monaco-action-bar .repl-panel-action-filter .monaco-inputbox { +.pane-header .monaco-action-bar .panel-action-tree-filter .monaco-inputbox { height: 20px; line-height: 18px; } -.monaco-workbench.vs .monaco-action-bar .repl-panel-action-filter .monaco-inputbox { +.monaco-workbench.vs .monaco-action-bar .panel-action-tree-filter .monaco-inputbox { height: 25px; } -.panel > .title .monaco-action-bar .action-item.repl-panel-action-filter-container { +.panel > .title .monaco-action-bar .action-item.panel-action-tree-filter-container { max-width: 600px; min-width: 300px; margin-right: 10px; } -.repl-tree .monaco-action-bar .action-item.repl-panel-action-filter-container, -.panel > .title .monaco-action-bar .action-item.repl-panel-action-filter-container.grow { +.monaco-action-bar .action-item.panel-action-tree-filter-container, +.panel > .title .monaco-action-bar .action-item.panel-action-tree-filter-container.grow { flex: 1; } diff --git a/src/vs/workbench/contrib/debug/browser/replFilterView.ts b/src/vs/workbench/contrib/treeFilter/browser/treeFilterView.ts similarity index 85% rename from src/vs/workbench/contrib/debug/browser/replFilterView.ts rename to src/vs/workbench/contrib/treeFilter/browser/treeFilterView.ts index a0b73db76e5..87b785171ff 100644 --- a/src/vs/workbench/contrib/debug/browser/replFilterView.ts +++ b/src/vs/workbench/contrib/treeFilter/browser/treeFilterView.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/replFilter'; +import 'vs/css!./media/treeFilter'; import * as DOM from 'vs/base/browser/dom'; import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { Delayer } from 'vs/base/common/async'; @@ -16,7 +16,8 @@ import { Event, Emitter } from 'vs/base/common/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { ContextScopedHistoryInputBox } from 'vs/platform/browser/contextScopedHistoryWidget'; -import { localize } from 'vs/nls'; +import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; export interface IReplFiltersChangeEvent { filterText?: boolean; @@ -29,7 +30,7 @@ export interface IReplFiltersOptions { layout: DOM.Dimension; } -export class ReplFilterState extends Disposable { +export class TreeFilterState extends Disposable { private readonly _onDidChange: Emitter = this._register(new Emitter()); readonly onDidChange: Event = this._onDidChange.event; @@ -66,7 +67,7 @@ export class ReplFilterState extends Disposable { } } -export class ReplFilterActionViewItem extends BaseActionViewItem { +export class TreeFilterPanelActionViewItem extends BaseActionViewItem { private delayedFilterUpdate: Delayer; private container: HTMLElement | undefined; @@ -74,8 +75,10 @@ export class ReplFilterActionViewItem extends BaseActionViewItem { constructor( action: IAction, - private filters: ReplFilterState, + private placeholder: string, + private filters: TreeFilterState, @IInstantiationService private readonly instantiationService: IInstantiationService, + @IThemeService private readonly themeService: IThemeService, @IContextViewService private readonly contextViewService: IContextViewService) { super(null, action); this.delayedFilterUpdate = new Delayer(200); @@ -84,12 +87,14 @@ export class ReplFilterActionViewItem extends BaseActionViewItem { render(container: HTMLElement): void { this.container = container; - DOM.addClass(this.container, 'repl-panel-action-filter-container'); + DOM.addClass(this.container, 'panel-action-tree-filter-container'); this.element = DOM.append(this.container, DOM.$('')); this.element.className = this.class; this.createInput(this.element); this.updateClass(); + + this.adjustInputBox(); } focus(): void { @@ -106,9 +111,10 @@ export class ReplFilterActionViewItem extends BaseActionViewItem { private createInput(container: HTMLElement): void { this.filterInputBox = this._register(this.instantiationService.createInstance(ContextScopedHistoryInputBox, container, this.contextViewService, { - placeholder: localize('workbench.debug.filter.placeholder', "Filter. E.g.: text, !exclude"), + placeholder: this.placeholder, history: this.filters.filterHistory })); + this._register(attachInputBoxStyler(this.filterInputBox, this.themeService)); this.filterInputBox.value = this.filters.filterText; this._register(this.filterInputBox.onDidChange(() => this.delayedFilterUpdate.trigger(() => this.onDidInputChange(this.filterInputBox!)))); this._register(this.filters.onDidChange((event: IReplFiltersChangeEvent) => { @@ -150,31 +156,34 @@ export class ReplFilterActionViewItem extends BaseActionViewItem { } private onInputKeyDown(event: StandardKeyboardEvent) { - let handled = false; if (event.equals(KeyCode.Escape)) { this.clearFilterText(); - handled = true; - } - if (handled) { event.stopPropagation(); event.preventDefault(); } } + private adjustInputBox(): void { + if (this.element && this.filterInputBox) { + this.filterInputBox.inputElement.style.paddingRight = DOM.hasClass(this.element, 'small') ? '25px' : '150px'; + } + } + protected updateClass(): void { if (this.element && this.container) { this.element.className = this.class; DOM.toggleClass(this.container, 'grow', DOM.hasClass(this.element, 'grow')); + this.adjustInputBox(); } } protected get class(): string { if (this.filters.layout.width > 800) { - return 'repl-panel-action-filter grow'; + return 'panel-action-tree-filter grow'; } else if (this.filters.layout.width < 600) { - return 'repl-panel-action-filter small'; + return 'panel-action-tree-filter small'; } else { - return 'repl-panel-action-filter'; + return 'panel-action-tree-filter'; } } } From b3c102bb6c4980a2e269139eada68a8cabc4b417 Mon Sep 17 00:00:00 2001 From: Aditya Thakral Date: Fri, 31 Jul 2020 02:31:22 -0400 Subject: [PATCH 261/736] Original settings editor as list implementation: Up + Down arrow keys work Manage tab order on setting trees Try to move up/down with focus on input Use only select Focus tweaks Make escape shortcut work Implement smaller virtual lists Sync TOC tree selection Add heading focus Remove unused import --- .../browser/media/settingsEditor2.css | 22 +- .../browser/preferences.contribution.ts | 36 ++- .../preferences/browser/settingsEditor2.ts | 220 ++++++++++-------- .../preferences/browser/settingsTree.ts | 106 +++++++-- .../preferences/browser/settingsTreeModels.ts | 19 +- .../contrib/preferences/browser/tocTree.ts | 30 ++- 6 files changed, 293 insertions(+), 140 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index 02f464009af..8f01999568a 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -299,8 +299,11 @@ 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 */ + border-radius: 5px; +} +.settings-editor > .settings-body > .settings-tree-container .monaco-list-row.focused.selected .setting-item-contents .setting-item-title { + padding-left: 7px; } @@ -528,12 +531,21 @@ } .settings-editor > .settings-body > .settings-tree-container .settings-group-title-label { + display: inline-block; margin: 0px; font-weight: 600; + height: 100%; + box-sizing: border-box; + border-radius: 5px; + padding: 10px; + padding-left: 0; +} + +.settings-editor > .settings-body > .settings-tree-container .monaco-list-row.selected .settings-group-title-label { + padding-left: 10px; } .settings-editor > .settings-body > .settings-tree-container .settings-group-level-1 { - padding-top: 23px; font-size: 24px; } @@ -542,10 +554,6 @@ font-size: 20px; } -.settings-editor > .settings-body > .settings-tree-container .settings-group-level-1.settings-group-first { - padding-top: 7px; -} - .settings-editor.search-mode > .settings-body .settings-toc-container .monaco-list-row .settings-toc-count { display: block; } diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index 5bfbe8133ab..a3835135c01 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -507,6 +507,14 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon } return null; } + + function settingsEditorFocusSearch(accessor: ServicesAccessor) { + const preferencesEditor = getPreferencesEditor(accessor); + if (preferencesEditor) { + preferencesEditor.focusSearch(); + } + } + registerAction2(class extends Action2 { constructor() { super({ @@ -521,12 +529,24 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon }); } - run(accessor: ServicesAccessor) { - const preferencesEditor = getPreferencesEditor(accessor); - if (preferencesEditor) { - preferencesEditor.focusSearch(); - } + run(accessor: ServicesAccessor) { settingsEditorFocusSearch(accessor); } + }); + + registerAction2(class extends Action2 { + constructor() { + super({ + id: SETTINGS_EDITOR_COMMAND_SEARCH, + precondition: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_TOC_ROW_FOCUS), + keybinding: { + primary: KeyCode.Escape, + weight: KeybindingWeight.WorkbenchContrib, + when: null + }, + title: nls.localize('settings.focusSearch', "Focus settings search") + }); } + + run(accessor: ServicesAccessor) { settingsEditorFocusSearch(accessor); } }); registerAction2(class extends Action2 { @@ -691,7 +711,11 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon constructor() { super({ id: SETTINGS_EDITOR_COMMAND_FOCUS_TOC, - precondition: CONTEXT_SETTINGS_EDITOR, + precondition: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_TOC_ROW_FOCUS.negate()), + keybinding: { + primary: KeyCode.Escape, + weight: KeybindingWeight.WorkbenchContrib, + }, title: nls.localize('settings.focusSettingsTOC', "Focus settings TOC tree") }); } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index a31a0fa0f9f..202f4cfe74f 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -20,7 +20,7 @@ import { Iterable } from 'vs/base/common/iterator'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; -import { isArray, withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types'; +import { isArray, isUndefinedOrNull, withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/settingsEditor2'; import { localize } from 'vs/nls'; @@ -43,9 +43,9 @@ import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { IEditorMemento, IEditorPane } from 'vs/workbench/common/editor'; import { attachSuggestEnabledInputBoxStyler, SuggestEnabledInput } from 'vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput'; import { SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; -import { commonlyUsedData, tocData } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; -import { AbstractSettingRenderer, ISettingLinkClickEvent, ISettingOverrideClickEvent, resolveExtensionsSettings, resolveSettingsTree, SettingsTree, SettingTreeRenderers } from 'vs/workbench/contrib/preferences/browser/settingsTree'; -import { ISettingsEditorViewState, parseQuery, SearchResultIdx, SearchResultModel, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; +import { commonlyUsedData, ITOCEntry, tocData } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; +import { AbstractSettingRenderer, ISettingLinkClickEvent, ISettingOverrideClickEvent, resolveExtensionsSettings, resolveSettingsTree, SettingsTree, SettingTreeRenderers, updateSettingTreeTabOrder } from 'vs/workbench/contrib/preferences/browser/settingsTree'; +import { ISettingsEditorViewState, parseQuery, SearchResultIdx, SearchResultModel, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeModel } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; import { settingsTextInputBorder } from 'vs/workbench/contrib/preferences/browser/settingsWidgets'; import { createTOCIterator, TOCTree, TOCTreeModel } from 'vs/workbench/contrib/preferences/browser/tocTree'; import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, EXTENSION_SETTING_TAG, IPreferencesSearchService, ISearchProvider, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; @@ -66,6 +66,26 @@ function createGroupIterator(group: SettingsTreeGroupElement): Iterable; private tocFocusedElement: SettingsTreeGroupElement | null = null; + private treeFocusedElement: SettingsTreeElement | null = null; private settingsTreeScrollTop = 0; private dimension!: DOM.Dimension; @@ -205,10 +227,14 @@ export class SettingsEditor2 extends BaseEditor { set minimumWidth(value: number) { /*noop*/ } set maximumWidth(value: number) { /*noop*/ } - private get currentSettingsModel() { + private get currentVisibleSettingsModel() { return this.searchResultModel || this.settingsTreeModel; } + private get currentAllSettingsModel() { + return this.searchResultModel || this.allSettingsModel; + } + private get searchResultModel(): SearchResultModel | null { return this._searchResultModel; } @@ -256,7 +282,7 @@ export class SettingsEditor2 extends BaseEditor { })); // Init TOC selection - this.updateTreeScrollSync(); + this.syncTOCTree(); }); }); } @@ -383,7 +409,8 @@ export class SettingsEditor2 extends BaseEditor { return; } - const elements = this.currentSettingsModel.getElementsByName(focusedKey); + // TODO@9at8 When does this happen? + const elements = this.currentAllSettingsModel.getElementsByName(focusedKey); if (elements && elements[0]) { this.settingRenderers.showContextMenu(elements[0], settingDOMElement); } @@ -495,7 +522,7 @@ export class SettingsEditor2 extends BaseEditor { } private onDidClickSetting(evt: ISettingLinkClickEvent, recursed?: boolean): void { - const elements = this.currentSettingsModel.getElementsByName(evt.targetKey); + const elements = this.currentAllSettingsModel.getElementsByName(evt.targetKey); if (elements && elements[0]) { let sourceTop = 0.5; try { @@ -507,7 +534,27 @@ export class SettingsEditor2 extends BaseEditor { // e.g. clicked a searched element, now the search has been cleared } - this.settingsTree.reveal(elements[0], sourceTop); + const visibleGroup = unwrapRootElement(this.settingsTreeModel.root); + const targetGroup = elements[0].parent; + + if (targetGroup && visibleGroup.id !== targetGroup.id) { + const targetGroupTOCEntry = this.allSettingsModel.getTOCEntryByGroupElement(targetGroup); + this.settingsTreeModel.update(createRootTOCEntry(targetGroupTOCEntry)); + this.refreshTree(); + this.syncTOCTree(); + } + + const targetElement = this.settingsTreeModel.getElementById(elements[0].id); + if (isUndefinedOrNull(targetElement)) { + return; + } + + this.settingsTree.reveal(targetElement, sourceTop); + + // We need to shift focus from the setting that contains the link to the setting that's + // linked. Clicking on the link sets focus on the setting that contains the link, + // which is why we need the setTimeout + setTimeout(() => this.settingsTree.setFocus([targetElement]), 50); const domElements = this.settingRenderers.getDOMElementsForSettingKey(this.settingsTree.getHTMLElement(), evt.targetKey); if (domElements && domElements[0]) { @@ -569,48 +616,7 @@ export class SettingsEditor2 extends BaseEditor { })); this.createTOC(bodyContainer); - - this.createFocusSink( - bodyContainer, - e => { - if (DOM.findParentWithClass(e.relatedTarget, 'settings-editor-tree')) { - if (this.settingsTree.scrollTop > 0) { - const firstElement = this.settingsTree.firstVisibleElement; - - if (typeof firstElement !== 'undefined') { - 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'); - this.createSettingsTree(bodyContainer); - - this.createFocusSink( - bodyContainer, - e => { - if (DOM.findParentWithClass(e.relatedTarget, 'settings-editor-tree')) { - if (this.settingsTree.scrollTop < this.settingsTree.scrollHeight) { - const lastElement = this.settingsTree.lastVisibleElement; - this.settingsTree.reveal(lastElement, 0.9); - return true; - } - } - - return false; - }, - 'settings list focus helper' - ); } private addCtrlAInterceptor(container: HTMLElement): void { @@ -628,19 +634,6 @@ export class SettingsEditor2 extends BaseEditor { })); } - 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 = this.instantiationService.createInstance(TOCTreeModel, this.viewState); this.tocTreeContainer = DOM.append(parent, $('.settings-toc-container')); @@ -667,7 +660,16 @@ export class SettingsEditor2 extends BaseEditor { this.settingsTree.scrollTop = 0; } } else if (element && (!e.browserEvent || !(e.browserEvent).fromScroll)) { - this.settingsTree.reveal(element, 0); + // The fact that this was focused means that it has a toc entry + const targetTOCEntry = this.allSettingsModel.getTOCEntryByGroupElement(element)!; + + this.settingsTreeModel.update(createRootTOCEntry(targetTOCEntry)); + this.refreshTree(); + + const visibleElement = this.settingsTreeModel.getElementById(element.id)!; + + this.settingsTree.reveal(visibleElement); + this.settingsTree.setFocus([visibleElement]); } })); @@ -717,7 +719,6 @@ export class SettingsEditor2 extends BaseEditor { this.settingsTreeContainer, this.viewState, this.settingRenderers.allRenderers)); - this.settingsTree.getHTMLElement().attributes.removeNamedItem('tabindex'); this._register(this.settingsTree.onDidScroll(() => { if (this.settingsTree.scrollTop === this.settingsTreeScrollTop) { @@ -725,12 +726,21 @@ export class SettingsEditor2 extends BaseEditor { } this.settingsTreeScrollTop = this.settingsTree.scrollTop; + updateSettingTreeTabOrder(this.settingsTreeContainer); + })); - // 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); + // There is no different select state in the settings tree + this._register(this.settingsTree.onDidChangeFocus(e => { + const element = e.elements[0]; + if (this.treeFocusedElement === element) { + return; + } + + this.treeFocusedElement = element; + this.settingsTree.setSelection(element ? [element] : []); + + // Wait for rendering to complete + setTimeout(() => updateSettingTreeTabOrder(this.settingsTreeContainer), 0); })); } @@ -756,35 +766,40 @@ export class SettingsEditor2 extends BaseEditor { } } - private updateTreeScrollSync(): void { + private syncTOCTree(): void { this.settingRenderers.cancelSuggesters(); - if (this.searchResultModel) { + if (this.searchResultModel || !this.tocTreeModel || !this.settingsTreeModel) { return; } - if (!this.tocTreeModel) { + const visibleGroup = unwrapRootElement(this.settingsTreeModel.root); + const tocSelectedGroup = this.tocTree.getSelection()[0]; + + if (visibleGroup.id === tocSelectedGroup?.id) { return; } - const elementToSync = this.settingsTree.firstVisibleElement; - const element = elementToSync instanceof SettingsTreeSettingElement ? elementToSync.parent : - elementToSync instanceof SettingsTreeGroupElement ? elementToSync : - null; + // visibleGroup and targetGroup are not referentially equal + const targetGroup = this.allSettingsModel.getElementById(visibleGroup.id); + + if (!(targetGroup instanceof SettingsTreeGroupElement)) { + return; + } // It's possible for this to be called when the TOC and settings tree are out of sync - e.g. when the settings tree has deferred a refresh because // it is focused. So, bail if element doesn't exist in the TOC. let nodeExists = true; - try { this.tocTree.getNode(element); } catch (e) { nodeExists = false; } + try { this.tocTree.getNode(targetGroup); } catch (e) { nodeExists = false; } if (!nodeExists) { return; } - if (element && this.tocTree.getSelection()[0] !== element) { - const ancestors = this.getAncestors(element); + if (this.tocTree.getSelection()[0] !== targetGroup) { + const ancestors = this.getAncestors(targetGroup); ancestors.forEach(e => this.tocTree.expand(e)); - this.tocTree.reveal(element); - const elementTop = this.tocTree.getRelativeTop(element); + this.tocTree.reveal(targetGroup); + const elementTop = this.tocTree.getRelativeTop(targetGroup); if (typeof elementTop !== 'number') { return; } @@ -793,18 +808,18 @@ export class SettingsEditor2 extends BaseEditor { ancestors.forEach(e => this.tocTree.expand(e)); if (elementTop < 0 || elementTop > 1) { - this.tocTree.reveal(element); + this.tocTree.reveal(targetGroup); } else { - this.tocTree.reveal(element, elementTop); + this.tocTree.reveal(targetGroup, elementTop); } - this.tocTree.expand(element); + this.tocTree.expand(targetGroup); - this.tocTree.setSelection([element]); + this.tocTree.setSelection([targetGroup]); const fakeKeyboardEvent = new KeyboardEvent('keydown'); (fakeKeyboardEvent).fromScroll = true; - this.tocTree.setFocus([element], fakeKeyboardEvent); + this.tocTree.setFocus([targetGroup], fakeKeyboardEvent); } } @@ -990,8 +1005,15 @@ export class SettingsEditor2 extends BaseEditor { this.searchResultModel.updateChildren(); } - if (this.settingsTreeModel) { - this.settingsTreeModel.update(resolvedSettingsRoot); + if (this.settingsTreeModel && this.allSettingsModel) { + this.allSettingsModel.update(resolvedSettingsRoot); + this.tocTreeModel.settingsTreeRoot = this.allSettingsModel.root; + + const visibleTOCEntry = this.allSettingsModel.getTOCEntryByGroupElement( + unwrapRootElement(this.settingsTreeModel.root), + ); + + this.settingsTreeModel.update(createRootTOCEntry(visibleTOCEntry)); if (schemaChange && !!this.searchResultModel) { // If an extension's settings were just loaded and a search is active, retrigger the search so it shows up @@ -1001,9 +1023,13 @@ export class SettingsEditor2 extends BaseEditor { this.refreshTOCTree(); this.renderTree(undefined, forceRefresh); } else { + this.allSettingsModel = this.instantiationService.createInstance(SettingsTreeModel, this.viewState); + this.allSettingsModel.update(resolvedSettingsRoot); + this.settingsTreeModel = this.instantiationService.createInstance(SettingsTreeModel, this.viewState); - this.settingsTreeModel.update(resolvedSettingsRoot); - this.tocTreeModel.settingsTreeRoot = this.settingsTreeModel.root as SettingsTreeGroupElement; + this.settingsTreeModel.update(createRootTOCEntry(commonlyUsed.tree)); + + this.tocTreeModel.settingsTreeRoot = this.allSettingsModel.root as SettingsTreeGroupElement; const cachedState = this.restoreCachedState(); if (cachedState && cachedState.searchQuery) { @@ -1026,6 +1052,10 @@ export class SettingsEditor2 extends BaseEditor { keys.forEach(key => this.settingsTreeModel.updateElementsByName(key)); } + if (this.allSettingsModel) { + keys.forEach(key => this.allSettingsModel.updateElementsByName(key)); + } + keys.forEach(key => this.renderTree(key)); } else { return this.renderTree(); @@ -1078,7 +1108,7 @@ export class SettingsEditor2 extends BaseEditor { this.renderResultCountMessages(); if (key) { - const elements = this.currentSettingsModel.getElementsByName(key); + const elements = this.currentVisibleSettingsModel.getElementsByName(key); if (elements && elements.length) { // TODO https://github.com/Microsoft/vscode/issues/57360 this.refreshTree(); @@ -1099,7 +1129,7 @@ export class SettingsEditor2 extends BaseEditor { private refreshTree(): void { if (this.isVisible()) { - this.settingsTree.setChildren(null, createGroupIterator(this.currentSettingsModel.root)); + this.settingsTree.setChildren(null, createGroupIterator(this.currentVisibleSettingsModel.root)); } } @@ -1111,7 +1141,7 @@ export class SettingsEditor2 extends BaseEditor { } private updateModifiedLabelForKey(key: string): void { - const dataElements = this.currentSettingsModel.getElementsByName(key); + const dataElements = this.currentVisibleSettingsModel.getElementsByName(key); const isModified = dataElements && dataElements[0] && dataElements[0].isConfigured; // all elements are either configured or not const elements = this.settingRenderers.getDOMElementsForSettingKey(this.settingsTree.getHTMLElement(), key); if (elements && elements[0]) { @@ -1316,7 +1346,7 @@ export class SettingsEditor2 extends BaseEditor { } private renderResultCountMessages() { - if (!this.currentSettingsModel) { + if (!this.currentVisibleSettingsModel) { return; } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index dc5a734b6f6..12d650962f1 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -5,7 +5,6 @@ import { BrowserFeatures } from 'vs/base/browser/canIUse'; import * as DOM from 'vs/base/browser/dom'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { alert as ariaAlert } from 'vs/base/browser/ui/aria/aria'; @@ -16,7 +15,7 @@ import { CachedListVirtualDelegate } 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'; +import { IObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTree'; import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel'; import { ITreeFilter, ITreeModel, ITreeNode, ITreeRenderer, TreeFilterResult, TreeVisibility } from 'vs/base/browser/ui/tree/tree'; import { Action, IAction, Separator } from 'vs/base/common/actions'; @@ -37,7 +36,7 @@ import { IContextMenuService, IContextViewService } from 'vs/platform/contextvie import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { editorBackground, errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground } from 'vs/platform/theme/common/colorRegistry'; +import { editorBackground, errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, listActiveSelectionBackground, listActiveSelectionForeground } from 'vs/platform/theme/common/colorRegistry'; import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler, attachStyler } from 'vs/platform/theme/common/styler'; import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { getIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge'; @@ -53,6 +52,9 @@ import { Codicon } from 'vs/base/common/codicons'; import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { IList } from 'vs/base/browser/ui/tree/indexTreeModel'; +import { IListService, WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; const $ = DOM.$; @@ -451,6 +453,45 @@ export interface ISettingOverrideClickEvent { targetKey: string; } +function removeChildrenFromTabOrder(node: Element): void { + const focusableElements = node.querySelectorAll(` + [tabindex="0"], + input:not([tabindex="-1"]), + select:not([tabindex="-1"]), + textarea:not([tabindex="-1"]), + a:not([tabindex="-1"]), + button:not([tabindex="-1"]), + area:not([tabindex="-1"]) + `); + + focusableElements.forEach(element => { + element.setAttribute(AbstractSettingRenderer.ELEMENT_FOCUSABLE_ATTR, 'true'); + element.setAttribute('tabindex', '-1'); + }); +} + +function addChildrenToTabOrder(node: Element): void { + const focusableElements = node.querySelectorAll( + `[${AbstractSettingRenderer.ELEMENT_FOCUSABLE_ATTR}="true"]` + ); + + focusableElements.forEach(element => { + element.removeAttribute(AbstractSettingRenderer.ELEMENT_FOCUSABLE_ATTR); + element.setAttribute('tabindex', '0'); + }); +} + +export function updateSettingTreeTabOrder(container: Element): void { + const allRows = [...container.querySelectorAll(AbstractSettingRenderer.ALL_ROWS_SELECTOR)]; + const focusedRow = allRows.find(row => row.classList.contains('focused')); + + allRows.forEach(removeChildrenFromTabOrder); + + if (isDefined(focusedRow)) { + addChildrenToTabOrder(focusedRow); + } +} + export abstract class AbstractSettingRenderer extends Disposable implements ITreeRenderer { /** To override */ abstract get templateId(): string; @@ -459,9 +500,11 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre static readonly CONTROL_SELECTOR = '.' + AbstractSettingRenderer.CONTROL_CLASS; static readonly CONTENTS_CLASS = 'setting-item-contents'; static readonly CONTENTS_SELECTOR = '.' + AbstractSettingRenderer.CONTENTS_CLASS; + static readonly ALL_ROWS_SELECTOR = '.monaco-list-row'; static readonly SETTING_KEY_ATTR = 'data-key'; static readonly SETTING_ID_ATTR = 'data-id'; + static readonly ELEMENT_FOCUSABLE_ATTR = 'data-focusable'; private readonly _onDidClickOverrideElement = this._register(new Emitter()); readonly onDidClickOverrideElement: Event = this._onDidClickOverrideElement.event; @@ -1248,6 +1291,15 @@ export class SettingTextRenderer extends AbstractSettingRenderer implements ITre })); common.toDispose.add(inputBox); inputBox.inputElement.classList.add(AbstractSettingRenderer.CONTROL_CLASS); + inputBox.inputElement.tabIndex = 0; + + // TODO@9at8: listWidget filters out all key events from input boxes, so we need to come up with a better way + // Disable ArrowUp and ArrowDown behaviour in favor of list navigation + common.toDispose.add(DOM.addStandardDisposableListener(inputBox.inputElement, DOM.EventType.KEY_DOWN, e => { + if (e.equals(KeyCode.UpArrow) || e.equals(KeyCode.DownArrow)) { + e.preventDefault(); + } + })); const template: ISettingTextItemTemplate = { ...common, @@ -1300,6 +1352,7 @@ export class SettingEnumRenderer extends AbstractSettingRenderer implements ITre const selectElement = common.controlElement.querySelector('select'); if (selectElement) { selectElement.classList.add(AbstractSettingRenderer.CONTROL_CLASS); + selectElement.tabIndex = 0; } common.toDispose.add( @@ -1392,6 +1445,7 @@ export class SettingNumberRenderer extends AbstractSettingRenderer implements IT })); common.toDispose.add(inputBox); inputBox.inputElement.classList.add(AbstractSettingRenderer.CONTROL_CLASS); + inputBox.inputElement.tabIndex = 0; const template: ISettingNumberItemTemplate = { ...common, @@ -1504,13 +1558,6 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre // Prevent clicks from being handled by list toDispose.add(DOM.addDisposableListener(controlElement, 'mousedown', (e: IMouseEvent) => e.stopPropagation())); - - toDispose.add(DOM.addStandardDisposableListener(controlElement, 'keydown', (e: StandardKeyboardEvent) => { - if (e.keyCode === KeyCode.Escape) { - e.browserEvent.stopPropagation(); - } - })); - toDispose.add(DOM.addDisposableListener(titleElement, DOM.EventType.MOUSE_ENTER, e => container.classList.add('mouseover'))); toDispose.add(DOM.addDisposableListener(titleElement, DOM.EventType.MOUSE_LEAVE, e => container.classList.remove('mouseover'))); @@ -1834,11 +1881,7 @@ class SettingsTreeDelegate extends CachedListVirtualDelegate extends ObjectTreeModel { } } -export class SettingsTree extends ObjectTree { +export class SettingsTree extends WorkbenchObjectTree { constructor( container: HTMLElement, viewState: ISettingsEditorViewState, renderers: ITreeRenderer[], + @IContextKeyService contextKeyService: IContextKeyService, + @IListService listService: IListService, @IThemeService themeService: IThemeService, @IConfigurationService configurationService: IConfigurationService, + @IKeybindingService keybindingService: IKeybindingService, + @IAccessibilityService accessibilityService: IAccessibilityService, @IInstantiationService instantiationService: IInstantiationService, ) { super('SettingsTree', container, @@ -1875,9 +1922,6 @@ export class SettingsTree extends ObjectTree { } }, accessibilityProvider: { - getWidgetRole() { - return 'form'; - }, getAriaLabel() { // TODO@roblourens https://github.com/microsoft/vscode/issues/95862 return ''; @@ -1889,9 +1933,16 @@ export class SettingsTree extends ObjectTree { styleController: id => new DefaultStyleController(DOM.createStyleSheet(container), id), filter: instantiationService.createInstance(SettingsTreeFilter, viewState), smoothScrolling: configurationService.getValue('workbench.list.smoothScrolling'), - }); + multipleSelectionSupport: false, + }, + contextKeyService, + listService, + themeService, + configurationService, + keybindingService, + accessibilityService, + ); - this.disposables.clear(); this.disposables.add(registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { const activeBorderColor = theme.getColor(focusBorder); if (activeBorderColor) { @@ -1940,6 +1991,19 @@ export class SettingsTree extends ObjectTree { if (focusBorderColor) { collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-markdown a:focus { outline-color: ${focusBorderColor} }`); } + + const listActiveSelectionBackgroundColor = theme.getColor(listActiveSelectionBackground); + if (listActiveSelectionBackgroundColor) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-list-row.selected .setting-item-contents .setting-item-title { background-color: ${listActiveSelectionBackgroundColor}; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-list-row.selected .settings-group-title-label { background-color: ${listActiveSelectionBackgroundColor}; }`); + } + + const listActiveSelectionForegroundColor = theme.getColor(listActiveSelectionForeground); + if (listActiveSelectionForegroundColor) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-list-row.selected .setting-item-contents .setting-item-title { color: ${listActiveSelectionForegroundColor}; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-list-row.selected .setting-item-label { color: ${listActiveSelectionForegroundColor}; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-list-row.selected .settings-group-title-label { color: ${listActiveSelectionForegroundColor}; }`); + } })); this.getHTMLElement().classList.add('settings-editor-tree'); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index f68d3c6d14a..2b5e5252902 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -284,6 +284,7 @@ export class SettingsTreeModel { protected _root!: SettingsTreeGroupElement; protected _treeElementsById = new Map(); private _treeElementsBySettingName = new Map(); + private _tocEntryByElementId = new Map(); private _tocRoot!: ITOCEntry; constructor( @@ -298,17 +299,14 @@ export class SettingsTreeModel { update(newTocRoot = this._tocRoot): void { this._treeElementsById.clear(); this._treeElementsBySettingName.clear(); + this._tocEntryByElementId.clear(); const newRoot = this.createSettingsTreeGroupElement(newTocRoot); if (newRoot.children[0] instanceof SettingsTreeGroupElement) { (newRoot.children[0]).isFirstGroup = true; // TODO } - if (this._root) { - this._root.children = newRoot.children; - } else { - this._root = newRoot; - } + this._root = newRoot; } getElementById(id: string): SettingsTreeElement | null { @@ -319,6 +317,16 @@ export class SettingsTreeModel { return withUndefinedAsNull(this._treeElementsBySettingName.get(name)); } + getTOCEntryByGroupElement(element: SettingsTreeGroupElement): ITOCEntry { + const tocEntry = this._tocEntryByElementId.get(element.id); + + if (isUndefinedOrNull(tocEntry)) { + throw new Error('Group element does not have a corresponding TOC entry'); + } + + return tocEntry; + } + updateElementsByName(name: string): void { if (!this._treeElementsBySettingName.has(name)) { return; @@ -351,6 +359,7 @@ export class SettingsTreeModel { element.children = children; this._treeElementsById.set(element.id, element); + this._tocEntryByElementId.set(element.id, tocEntry); return element; } diff --git a/src/vs/workbench/contrib/preferences/browser/tocTree.ts b/src/vs/workbench/contrib/preferences/browser/tocTree.ts index b8bc079ae71..10dbce57091 100644 --- a/src/vs/workbench/contrib/preferences/browser/tocTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/tocTree.ts @@ -6,7 +6,6 @@ import * as DOM from 'vs/base/browser/dom'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { DefaultStyleController, IListAccessibilityProvider } 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 { Iterable } from 'vs/base/common/iterator'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -18,6 +17,11 @@ import { ISettingsEditorViewState, SearchResultModel, SettingsTreeElement, Setti import { settingsHeaderForeground } from 'vs/workbench/contrib/preferences/browser/settingsWidgets'; import { localize } from 'vs/nls'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IListService, IWorkbenchObjectTreeOptions, WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; const $ = DOM.$; @@ -187,17 +191,22 @@ class SettingsAccessibilityProvider implements IListAccessibilityProvider { +export class TOCTree extends WorkbenchObjectTree { constructor( container: HTMLElement, viewState: ISettingsEditorViewState, + @IContextKeyService contextKeyService: IContextKeyService, + @IListService listService: IListService, @IThemeService themeService: IThemeService, - @IInstantiationService instantiationService: IInstantiationService + @IConfigurationService configurationService: IConfigurationService, + @IKeybindingService keybindingService: IKeybindingService, + @IAccessibilityService accessibilityService: IAccessibilityService, + @IInstantiationService instantiationService: IInstantiationService, ) { // test open mode const filter = instantiationService.createInstance(SettingsTreeFilter, viewState); - const options: IObjectTreeOptions = { + const options: IWorkbenchObjectTreeOptions = { filter, multipleSelectionSupport: false, identityProvider: { @@ -210,10 +219,19 @@ export class TOCTree extends ObjectTree { collapseByDefault: true }; - super('SettingsTOC', container, + super( + 'SettingsTOC', + container, new TOCTreeDelegate(), [new TOCRenderer()], - options); + options, + contextKeyService, + listService, + themeService, + configurationService, + keybindingService, + accessibilityService, + ); this.disposables.add(attachStyler(themeService, { listBackground: editorBackground, From e8c0b5043d75db8b6e315686dffaf06e20e5f70f Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 10 Aug 2020 16:32:08 -0700 Subject: [PATCH 262/736] Tweaks to settings editor as list implementation: Revert "Sync TOC tree selection" Revert "Implement smaller virtual lists" Fix broken scroll sync --- .../preferences/browser/settingsEditor2.ts | 147 +++++------------- .../preferences/browser/settingsTreeModels.ts | 19 +-- 2 files changed, 47 insertions(+), 119 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 202f4cfe74f..8bc5cdd95c1 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -20,7 +20,7 @@ import { Iterable } from 'vs/base/common/iterator'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; -import { isArray, isUndefinedOrNull, withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types'; +import { isArray, withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/settingsEditor2'; import { localize } from 'vs/nls'; @@ -43,9 +43,9 @@ import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { IEditorMemento, IEditorPane } from 'vs/workbench/common/editor'; import { attachSuggestEnabledInputBoxStyler, SuggestEnabledInput } from 'vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput'; import { SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; -import { commonlyUsedData, ITOCEntry, tocData } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; +import { commonlyUsedData, tocData } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; import { AbstractSettingRenderer, ISettingLinkClickEvent, ISettingOverrideClickEvent, resolveExtensionsSettings, resolveSettingsTree, SettingsTree, SettingTreeRenderers, updateSettingTreeTabOrder } from 'vs/workbench/contrib/preferences/browser/settingsTree'; -import { ISettingsEditorViewState, parseQuery, SearchResultIdx, SearchResultModel, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeModel } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; +import { ISettingsEditorViewState, parseQuery, SearchResultIdx, SearchResultModel, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; import { settingsTextInputBorder } from 'vs/workbench/contrib/preferences/browser/settingsWidgets'; import { createTOCIterator, TOCTree, TOCTreeModel } from 'vs/workbench/contrib/preferences/browser/tocTree'; import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, EXTENSION_SETTING_TAG, IPreferencesSearchService, ISearchProvider, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; @@ -66,26 +66,6 @@ function createGroupIterator(group: SettingsTreeGroupElement): Iterable this.settingsTree.setFocus([targetElement]), 50); + setTimeout(() => this.settingsTree.setFocus([elements[0]]), 50); const domElements = this.settingRenderers.getDOMElementsForSettingKey(this.settingsTree.getHTMLElement(), evt.targetKey); if (domElements && domElements[0]) { @@ -660,16 +619,8 @@ export class SettingsEditor2 extends BaseEditor { this.settingsTree.scrollTop = 0; } } else if (element && (!e.browserEvent || !(e.browserEvent).fromScroll)) { - // The fact that this was focused means that it has a toc entry - const targetTOCEntry = this.allSettingsModel.getTOCEntryByGroupElement(element)!; - - this.settingsTreeModel.update(createRootTOCEntry(targetTOCEntry)); - this.refreshTree(); - - const visibleElement = this.settingsTreeModel.getElementById(element.id)!; - - this.settingsTree.reveal(visibleElement); - this.settingsTree.setFocus([visibleElement]); + this.settingsTree.reveal(element, 0); + this.settingsTree.setFocus([element]); } })); @@ -727,6 +678,12 @@ export class SettingsEditor2 extends BaseEditor { this.settingsTreeScrollTop = this.settingsTree.scrollTop; updateSettingTreeTabOrder(this.settingsTreeContainer); + + // 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); })); // There is no different select state in the settings tree @@ -766,40 +723,35 @@ export class SettingsEditor2 extends BaseEditor { } } - private syncTOCTree(): void { + private updateTreeScrollSync(): void { this.settingRenderers.cancelSuggesters(); - if (this.searchResultModel || !this.tocTreeModel || !this.settingsTreeModel) { + if (this.searchResultModel) { return; } - const visibleGroup = unwrapRootElement(this.settingsTreeModel.root); - const tocSelectedGroup = this.tocTree.getSelection()[0]; - - if (visibleGroup.id === tocSelectedGroup?.id) { + if (!this.tocTreeModel) { return; } - // visibleGroup and targetGroup are not referentially equal - const targetGroup = this.allSettingsModel.getElementById(visibleGroup.id); - - if (!(targetGroup instanceof SettingsTreeGroupElement)) { - return; - } + const elementToSync = this.settingsTree.firstVisibleElement; + const element = elementToSync instanceof SettingsTreeSettingElement ? elementToSync.parent : + elementToSync instanceof SettingsTreeGroupElement ? elementToSync : + null; // It's possible for this to be called when the TOC and settings tree are out of sync - e.g. when the settings tree has deferred a refresh because // it is focused. So, bail if element doesn't exist in the TOC. let nodeExists = true; - try { this.tocTree.getNode(targetGroup); } catch (e) { nodeExists = false; } + try { this.tocTree.getNode(element); } catch (e) { nodeExists = false; } if (!nodeExists) { return; } - if (this.tocTree.getSelection()[0] !== targetGroup) { - const ancestors = this.getAncestors(targetGroup); + if (element && this.tocTree.getSelection()[0] !== element) { + const ancestors = this.getAncestors(element); ancestors.forEach(e => this.tocTree.expand(e)); - this.tocTree.reveal(targetGroup); - const elementTop = this.tocTree.getRelativeTop(targetGroup); + this.tocTree.reveal(element); + const elementTop = this.tocTree.getRelativeTop(element); if (typeof elementTop !== 'number') { return; } @@ -808,18 +760,18 @@ export class SettingsEditor2 extends BaseEditor { ancestors.forEach(e => this.tocTree.expand(e)); if (elementTop < 0 || elementTop > 1) { - this.tocTree.reveal(targetGroup); + this.tocTree.reveal(element); } else { - this.tocTree.reveal(targetGroup, elementTop); + this.tocTree.reveal(element, elementTop); } - this.tocTree.expand(targetGroup); + this.tocTree.expand(element); - this.tocTree.setSelection([targetGroup]); + this.tocTree.setSelection([element]); const fakeKeyboardEvent = new KeyboardEvent('keydown'); (fakeKeyboardEvent).fromScroll = true; - this.tocTree.setFocus([targetGroup], fakeKeyboardEvent); + this.tocTree.setFocus([element], fakeKeyboardEvent); } } @@ -1005,15 +957,8 @@ export class SettingsEditor2 extends BaseEditor { this.searchResultModel.updateChildren(); } - if (this.settingsTreeModel && this.allSettingsModel) { - this.allSettingsModel.update(resolvedSettingsRoot); - this.tocTreeModel.settingsTreeRoot = this.allSettingsModel.root; - - const visibleTOCEntry = this.allSettingsModel.getTOCEntryByGroupElement( - unwrapRootElement(this.settingsTreeModel.root), - ); - - this.settingsTreeModel.update(createRootTOCEntry(visibleTOCEntry)); + if (this.settingsTreeModel) { + this.settingsTreeModel.update(resolvedSettingsRoot); if (schemaChange && !!this.searchResultModel) { // If an extension's settings were just loaded and a search is active, retrigger the search so it shows up @@ -1023,13 +968,9 @@ export class SettingsEditor2 extends BaseEditor { this.refreshTOCTree(); this.renderTree(undefined, forceRefresh); } else { - this.allSettingsModel = this.instantiationService.createInstance(SettingsTreeModel, this.viewState); - this.allSettingsModel.update(resolvedSettingsRoot); - this.settingsTreeModel = this.instantiationService.createInstance(SettingsTreeModel, this.viewState); - this.settingsTreeModel.update(createRootTOCEntry(commonlyUsed.tree)); - - this.tocTreeModel.settingsTreeRoot = this.allSettingsModel.root as SettingsTreeGroupElement; + this.settingsTreeModel.update(resolvedSettingsRoot); + this.tocTreeModel.settingsTreeRoot = this.settingsTreeModel.root as SettingsTreeGroupElement; const cachedState = this.restoreCachedState(); if (cachedState && cachedState.searchQuery) { @@ -1052,10 +993,6 @@ export class SettingsEditor2 extends BaseEditor { keys.forEach(key => this.settingsTreeModel.updateElementsByName(key)); } - if (this.allSettingsModel) { - keys.forEach(key => this.allSettingsModel.updateElementsByName(key)); - } - keys.forEach(key => this.renderTree(key)); } else { return this.renderTree(); @@ -1108,7 +1045,7 @@ export class SettingsEditor2 extends BaseEditor { this.renderResultCountMessages(); if (key) { - const elements = this.currentVisibleSettingsModel.getElementsByName(key); + const elements = this.currentSettingsModel.getElementsByName(key); if (elements && elements.length) { // TODO https://github.com/Microsoft/vscode/issues/57360 this.refreshTree(); @@ -1129,7 +1066,7 @@ export class SettingsEditor2 extends BaseEditor { private refreshTree(): void { if (this.isVisible()) { - this.settingsTree.setChildren(null, createGroupIterator(this.currentVisibleSettingsModel.root)); + this.settingsTree.setChildren(null, createGroupIterator(this.currentSettingsModel.root)); } } @@ -1141,7 +1078,7 @@ export class SettingsEditor2 extends BaseEditor { } private updateModifiedLabelForKey(key: string): void { - const dataElements = this.currentVisibleSettingsModel.getElementsByName(key); + const dataElements = this.currentSettingsModel.getElementsByName(key); const isModified = dataElements && dataElements[0] && dataElements[0].isConfigured; // all elements are either configured or not const elements = this.settingRenderers.getDOMElementsForSettingKey(this.settingsTree.getHTMLElement(), key); if (elements && elements[0]) { @@ -1346,7 +1283,7 @@ export class SettingsEditor2 extends BaseEditor { } private renderResultCountMessages() { - if (!this.currentVisibleSettingsModel) { + if (!this.currentSettingsModel) { return; } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index 2b5e5252902..f68d3c6d14a 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -284,7 +284,6 @@ export class SettingsTreeModel { protected _root!: SettingsTreeGroupElement; protected _treeElementsById = new Map(); private _treeElementsBySettingName = new Map(); - private _tocEntryByElementId = new Map(); private _tocRoot!: ITOCEntry; constructor( @@ -299,14 +298,17 @@ export class SettingsTreeModel { update(newTocRoot = this._tocRoot): void { this._treeElementsById.clear(); this._treeElementsBySettingName.clear(); - this._tocEntryByElementId.clear(); const newRoot = this.createSettingsTreeGroupElement(newTocRoot); if (newRoot.children[0] instanceof SettingsTreeGroupElement) { (newRoot.children[0]).isFirstGroup = true; // TODO } - this._root = newRoot; + if (this._root) { + this._root.children = newRoot.children; + } else { + this._root = newRoot; + } } getElementById(id: string): SettingsTreeElement | null { @@ -317,16 +319,6 @@ export class SettingsTreeModel { return withUndefinedAsNull(this._treeElementsBySettingName.get(name)); } - getTOCEntryByGroupElement(element: SettingsTreeGroupElement): ITOCEntry { - const tocEntry = this._tocEntryByElementId.get(element.id); - - if (isUndefinedOrNull(tocEntry)) { - throw new Error('Group element does not have a corresponding TOC entry'); - } - - return tocEntry; - } - updateElementsByName(name: string): void { if (!this._treeElementsBySettingName.has(name)) { return; @@ -359,7 +351,6 @@ export class SettingsTreeModel { element.children = children; this._treeElementsById.set(element.id, element); - this._tocEntryByElementId.set(element.id, tocEntry); return element; } From 7e868f45dd52fe67945bb5afafe6ad6c0830917b Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 11 Aug 2020 15:06:06 -0700 Subject: [PATCH 263/736] Tweaks to the list row focus settings list - Change styling to look like notebooks - Add hover decoration - Add inactive focus styling - Keybindings/interaction tweaks - Make toolbar gear show up when row is focused, and make it tabbable - Revert "custom select box: react on KEY_UP to be aligned with actionBar so appropriate event gets canceled" This reverts commit 1a2a371c487d29b6dec33579c5f106f53a9d7fb7. --- .../browser/media/settingsEditor2.css | 47 +++++++----- .../browser/preferences.contribution.ts | 74 +++++++++++++++++-- .../preferences/browser/settingsEditor2.ts | 18 ++++- .../preferences/browser/settingsTree.ts | 43 +++++++---- .../preferences/browser/settingsWidgets.ts | 31 ++++++-- 5 files changed, 162 insertions(+), 51 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index 8f01999568a..8b5017293dc 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -175,14 +175,14 @@ .settings-editor > .settings-body .settings-tree-container .setting-toolbar-container { position: absolute; - left: -32px; + left: -22px; top: 11px; bottom: 0px; width: 26px; } .settings-editor > .settings-body .settings-tree-container .monaco-list-row .mouseover .setting-toolbar-container > .monaco-toolbar .codicon, -.settings-editor > .settings-body .settings-tree-container .monaco-list-row .setting-item-contents.focused .setting-toolbar-container > .monaco-toolbar .codicon, +.settings-editor > .settings-body .settings-tree-container .monaco-list-row.focused .setting-item-contents .setting-toolbar-container > .monaco-toolbar .codicon, .settings-editor > .settings-body .settings-tree-container .monaco-list-row .setting-toolbar-container:hover > .monaco-toolbar .codicon, .settings-editor > .settings-body .settings-tree-container .monaco-list-row .setting-toolbar-container > .monaco-toolbar .active .codicon { opacity: 1; @@ -283,15 +283,34 @@ max-width: 1000px; margin: auto; box-sizing: border-box; - padding-left: 219px; - padding-right: 20px; + padding-left: 204px; + padding-right: 5px; overflow: visible; } +.settings-editor > .settings-body > .settings-tree-container .settings-group-title-label::before, +.settings-editor > .settings-body > .settings-tree-container .settings-group-title-label::after, +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents::before, +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents::after { + content: ' '; + position: absolute; + left: 0px; + right: 0px; +} + +.settings-editor > .settings-body > .settings-tree-container .settings-group-title-label::before, +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents::before { + top: 0px; +} + +.settings-editor > .settings-body > .settings-tree-container .settings-group-title-label::after, +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents::after { + bottom: 0px; +} + .settings-editor > .settings-body > .settings-tree-container .setting-item-contents { position: relative; - padding-top: 12px; - padding-bottom: 18px; + padding: 12px 15px 18px; white-space: normal; } @@ -300,12 +319,7 @@ overflow: hidden; text-overflow: ellipsis; display: inline-block; /* size to contents for hover to show context button */ - border-radius: 5px; } -.settings-editor > .settings-body > .settings-tree-container .monaco-list-row.focused.selected .setting-item-contents .setting-item-title { - padding-left: 7px; -} - .settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-modified-indicator { display: none; @@ -318,7 +332,7 @@ width: 6px; border-left-width: 2px; border-left-style: solid; - left: -9px; + left: 5px; top: 15px; bottom: 16px; } @@ -536,13 +550,10 @@ font-weight: 600; height: 100%; box-sizing: border-box; - border-radius: 5px; padding: 10px; - padding-left: 0; -} - -.settings-editor > .settings-body > .settings-tree-container .monaco-list-row.selected .settings-group-title-label { - padding-left: 10px; + padding-left: 15px; + width: 100%; + position: relative; } .settings-editor > .settings-body > .settings-tree-container .settings-group-level-1 { diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index a3835135c01..5624c5cd5e9 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -12,7 +12,7 @@ import * as nls from 'vs/nls'; import { Action2, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { IsMacNativeContext } from 'vs/platform/contextkey/common/contextkeys'; +import { InputFocusedContext, IsMacNativeContext } from 'vs/platform/contextkey/common/contextkeys'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -50,6 +50,8 @@ const SETTINGS_EDITOR_COMMAND_EDIT_FOCUSED_SETTING = 'settings.action.editFocuse const SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH = 'settings.action.focusSettingsFromSearch'; const SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_LIST = 'settings.action.focusSettingsList'; const SETTINGS_EDITOR_COMMAND_FOCUS_TOC = 'settings.action.focusTOC'; +const SETTINGS_EDITOR_COMMAND_FOCUS_TOC2 = 'settings.action.focusTOC2'; +const SETTINGS_EDITOR_COMMAND_FOCUS_CONTROL = 'settings.action.focusSettingControl'; const SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON = 'settings.switchToJSON'; const SETTINGS_EDITOR_COMMAND_FILTER_MODIFIED = 'settings.filterByModified'; @@ -711,20 +713,76 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon constructor() { super({ id: SETTINGS_EDITOR_COMMAND_FOCUS_TOC, - precondition: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_TOC_ROW_FOCUS.negate()), - keybinding: { - primary: KeyCode.Escape, - weight: KeybindingWeight.WorkbenchContrib, - }, + keybinding: [ + { + primary: KeyCode.Escape, + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_TOC_ROW_FOCUS.negate()), + }, + { + primary: KeyCode.LeftArrow, + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_TOC_ROW_FOCUS.negate(), InputFocusedContext.negate()) + }], title: nls.localize('settings.focusSettingsTOC', "Focus settings TOC tree") }); } run(accessor: ServicesAccessor): void { const preferencesEditor = getPreferencesEditor(accessor); - if (preferencesEditor instanceof SettingsEditor2) { - preferencesEditor.focusTOC(); + if (!(preferencesEditor instanceof SettingsEditor2)) { + return; } + + if (document.activeElement?.classList.contains('monaco-list')) { + preferencesEditor.focusTOC(); + } else { + preferencesEditor.focusSettings(); + } + } + }); + + registerAction2(class extends Action2 { + constructor() { + super({ + id: SETTINGS_EDITOR_COMMAND_FOCUS_CONTROL, + precondition: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_TOC_ROW_FOCUS.negate()), + keybinding: { + primary: KeyCode.Enter, + weight: KeybindingWeight.WorkbenchContrib, + }, + title: nls.localize('settings.focusSettingControl', "Focus setting control") + }); + } + + run(accessor: ServicesAccessor): void { + const preferencesEditor = getPreferencesEditor(accessor); + if (!(preferencesEditor instanceof SettingsEditor2)) { + return; + } + + if (document.activeElement?.classList.contains('monaco-list')) { + preferencesEditor.focusSettings(true); + } + } + }); + + registerAction2(class extends Action2 { + constructor() { + super({ + id: SETTINGS_EDITOR_COMMAND_FOCUS_TOC2, + + title: nls.localize('settings.focusSettingsTOC', "Focus settings TOC tree") + }); + } + + run(accessor: ServicesAccessor): void { + const preferencesEditor = getPreferencesEditor(accessor); + if (!(preferencesEditor instanceof SettingsEditor2)) { + return; + } + + preferencesEditor.focusTOC(); } }); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 8bc5cdd95c1..73bd4acfad6 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -348,7 +348,8 @@ export class SettingsEditor2 extends BaseEditor { } } - focusSettings(): void { + focusSettings(focusSettingInput = false): void { + // TODO@roblourens is this in the right place? // Update ARIA global labels const labelElement = this.settingsAriaExtraLabelsContainer.querySelector('#settings_aria_more_actions_shortcut_label'); if (labelElement) { @@ -358,9 +359,18 @@ export class SettingsEditor2 extends BaseEditor { } } - const firstFocusable = this.settingsTree.getHTMLElement().querySelector(AbstractSettingRenderer.CONTROL_SELECTOR); - if (firstFocusable) { - (firstFocusable).focus(); + const focused = this.settingsTree.getFocus(); + if (!focused.length) { + this.settingsTree.focusFirst(); + } + + this.settingsTree.domFocus(); + + if (focusSettingInput) { + const controlInFocusedRow = this.settingsTree.getHTMLElement().querySelector(`.focused ${AbstractSettingRenderer.CONTROL_SELECTOR}`); + if (controlInFocusedRow) { + (controlInFocusedRow).focus(); + } } } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 12d650962f1..eb3a268f054 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -36,13 +36,13 @@ import { IContextMenuService, IContextViewService } from 'vs/platform/contextvie import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { editorBackground, errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, listActiveSelectionBackground, listActiveSelectionForeground } from 'vs/platform/theme/common/colorRegistry'; +import { editorBackground, errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground } from 'vs/platform/theme/common/colorRegistry'; import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler, attachStyler } from 'vs/platform/theme/common/styler'; import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { getIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge'; import { ITOCEntry } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; import { ISettingsEditorViewState, settingKeyToDisplayFormat, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeNewExtensionsElement, SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; -import { ExcludeSettingWidget, ISettingListChangeEvent, IListDataItem, ListSettingWidget, settingsHeaderForeground, settingsNumberInputBackground, settingsNumberInputBorder, settingsNumberInputForeground, settingsSelectBackground, settingsSelectBorder, settingsSelectForeground, settingsSelectListBorder, settingsTextInputBackground, settingsTextInputBorder, settingsTextInputForeground, ObjectSettingWidget, IObjectDataItem, IObjectEnumOption, ObjectValue, IObjectValueSuggester, IObjectKeySuggester } from 'vs/workbench/contrib/preferences/browser/settingsWidgets'; +import { ExcludeSettingWidget, ISettingListChangeEvent, IListDataItem, ListSettingWidget, settingsNumberInputBackground, settingsNumberInputBorder, settingsNumberInputForeground, settingsSelectBackground, settingsSelectBorder, settingsSelectForeground, settingsSelectListBorder, settingsTextInputBackground, settingsTextInputBorder, settingsTextInputForeground, ObjectSettingWidget, IObjectDataItem, IObjectEnumOption, ObjectValue, IObjectValueSuggester, IObjectKeySuggester, focusedRowBackground, focusedRowBorder, settingsHeaderForeground, rowHoverBackground } from 'vs/workbench/contrib/preferences/browser/settingsWidgets'; import { SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ISetting, ISettingsGroup, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; @@ -650,7 +650,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre private fixToolbarIcon(toolbar: ToolBar): void { const button = toolbar.getElement().querySelector('.codicon-toolbar-more'); if (button) { - (button).tabIndex = -1; + (button).tabIndex = 0; // change icon from ellipsis to gear (button).classList.add('codicon-gear'); @@ -1981,6 +1981,26 @@ export class SettingsTree extends WorkbenchObjectTree { 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}; }`); } + const focusedRowBackgroundColor = theme.getColor(focusedRowBackground); + if (focusedRowBackgroundColor) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-list-row.focused .setting-item-contents, + .settings-editor > .settings-body > .settings-tree-container .monaco-list-row.focused .settings-group-title-label { background-color: ${focusedRowBackgroundColor}; }`); + } + + const rowHoverBackgroundColor = theme.getColor(rowHoverBackground); + if (rowHoverBackgroundColor) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-list-row .setting-item-contents:hover, + .settings-editor > .settings-body > .settings-tree-container .monaco-list-row .settings-group-title-label:hover { background-color: ${rowHoverBackgroundColor}; }`); + } + + const focusedRowBorderColor = theme.getColor(focusedRowBorder); + if (focusedRowBorderColor) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-list:focus-within .monaco-list-row.focused .setting-item-contents::before, + .settings-editor > .settings-body > .settings-tree-container .monaco-list:focus-within .monaco-list-row.focused .setting-item-contents::after { border-top: 1px solid ${focusedRowBorderColor} }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-list:focus-within .monaco-list-row.focused .settings-group-title-label::before, + .settings-editor > .settings-body > .settings-tree-container .monaco-list:focus-within .monaco-list-row.focused .settings-group-title-label::after { border-top: 1px solid ${focusedRowBorderColor} }`); + } + const headerForegroundColor = theme.getColor(settingsHeaderForeground); if (headerForegroundColor) { collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .settings-group-title-label { color: ${headerForegroundColor}; }`); @@ -1992,18 +2012,11 @@ export class SettingsTree extends WorkbenchObjectTree { collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-markdown a:focus { outline-color: ${focusBorderColor} }`); } - const listActiveSelectionBackgroundColor = theme.getColor(listActiveSelectionBackground); - if (listActiveSelectionBackgroundColor) { - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-list-row.selected .setting-item-contents .setting-item-title { background-color: ${listActiveSelectionBackgroundColor}; }`); - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-list-row.selected .settings-group-title-label { background-color: ${listActiveSelectionBackgroundColor}; }`); - } - - const listActiveSelectionForegroundColor = theme.getColor(listActiveSelectionForeground); - if (listActiveSelectionForegroundColor) { - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-list-row.selected .setting-item-contents .setting-item-title { color: ${listActiveSelectionForegroundColor}; }`); - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-list-row.selected .setting-item-label { color: ${listActiveSelectionForegroundColor}; }`); - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-list-row.selected .settings-group-title-label { color: ${listActiveSelectionForegroundColor}; }`); - } + // const listActiveSelectionBackgroundColor = theme.getColor(listActiveSelectionBackground); + // if (listActiveSelectionBackgroundColor) { + // collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-list-row.selected .setting-item-contents .setting-item-title { background-color: ${listActiveSelectionBackgroundColor}; }`); + // collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-list-row.selected .settings-group-title-label { background-color: ${listActiveSelectionBackgroundColor}; }`); + // } })); this.getHTMLElement().classList.add('settings-editor-tree'); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts index d7f85b56922..5eaab66e56c 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts @@ -16,7 +16,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import 'vs/css!./media/settingsWidgets'; import { localize } from 'vs/nls'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { foreground, inputBackground, inputBorder, inputForeground, listActiveSelectionBackground, listActiveSelectionForeground, listHoverBackground, listHoverForeground, listInactiveSelectionBackground, listInactiveSelectionForeground, registerColor, selectBackground, selectBorder, selectForeground, textLinkForeground, textPreformatForeground, editorWidgetBorder, textLinkActiveForeground, simpleCheckboxBackground, simpleCheckboxForeground, simpleCheckboxBorder } from 'vs/platform/theme/common/colorRegistry'; +import { foreground, inputBorder, inputForeground, listActiveSelectionBackground, listActiveSelectionForeground, listHoverBackground, listHoverForeground, listInactiveSelectionBackground, listInactiveSelectionForeground, registerColor, selectBackground, selectBorder, selectForeground, textLinkForeground, textPreformatForeground, editorWidgetBorder, textLinkActiveForeground, simpleCheckboxBackground, simpleCheckboxForeground, simpleCheckboxBorder, listFocusBackground, transparent, focusBorder } from 'vs/platform/theme/common/colorRegistry'; import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler } from 'vs/platform/theme/common/styler'; import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { disposableTimeout } from 'vs/base/common/async'; @@ -25,6 +25,7 @@ import { preferencesEditIcon } from 'vs/workbench/contrib/preferences/browser/pr import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; import { isIOS } from 'vs/base/common/platform'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; +import { PANEL_BORDER } from 'vs/workbench/common/theme'; const $ = DOM.$; export const settingsHeaderForeground = registerColor('settings.headerForeground', { light: '#444444', dark: '#e7e7e7', hc: '#ffffff' }, localize('headerForeground', "The foreground color for a section header or active title.")); @@ -46,15 +47,33 @@ export const settingsCheckboxForeground = registerColor('settings.checkboxForegr export const settingsCheckboxBorder = registerColor('settings.checkboxBorder', { dark: simpleCheckboxBorder, light: simpleCheckboxBorder, hc: simpleCheckboxBorder }, localize('settingsCheckboxBorder', "Settings editor checkbox border.")); // Text control colors -export const settingsTextInputBackground = registerColor('settings.textInputBackground', { dark: inputBackground, light: inputBackground, hc: inputBackground }, localize('textInputBoxBackground', "Settings editor text input box background.")); +export const settingsTextInputBackground = settingsSelectBackground; //registerColor('settings.textInputBackground', { dark: inputBackground, light: inputBackground, hc: inputBackground }, localize('textInputBoxBackground', "Settings editor text input box background.")); export const settingsTextInputForeground = registerColor('settings.textInputForeground', { dark: inputForeground, light: inputForeground, hc: inputForeground }, localize('textInputBoxForeground', "Settings editor text input box foreground.")); export const settingsTextInputBorder = registerColor('settings.textInputBorder', { dark: inputBorder, light: inputBorder, hc: inputBorder }, localize('textInputBoxBorder', "Settings editor text input box border.")); // Number control colors -export const settingsNumberInputBackground = registerColor('settings.numberInputBackground', { dark: inputBackground, light: inputBackground, hc: inputBackground }, localize('numberInputBoxBackground', "Settings editor number input box background.")); +export const settingsNumberInputBackground = settingsSelectBackground; // registerColor('settings.numberInputBackground', { dark: inputBackground, light: inputBackground, hc: inputBackground }, localize('numberInputBoxBackground', "Settings editor number input box background.")); export const settingsNumberInputForeground = registerColor('settings.numberInputForeground', { dark: inputForeground, light: inputForeground, hc: inputForeground }, localize('numberInputBoxForeground', "Settings editor number input box foreground.")); export const settingsNumberInputBorder = registerColor('settings.numberInputBorder', { dark: inputBorder, light: inputBorder, hc: inputBorder }, localize('numberInputBoxBorder', "Settings editor number input box border.")); +export const focusedRowBackground = registerColor('settings.focusedRowBackground', { + dark: transparent(PANEL_BORDER, .4), + light: transparent(listFocusBackground, .4), + hc: null +}, localize('focusedRowBackground', "The background color of a cell when the row is focused.")); + +export const rowHoverBackground = registerColor('notebook.rowHoverBackground', { + dark: transparent(focusedRowBackground, .5), + light: transparent(focusedRowBackground, .7), + hc: null +}, localize('notebook.rowHoverBackground', "The background color of a row when the row is hovered.")); + +export const focusedRowBorder = registerColor('notebook.focusedRowBorder', { + dark: Color.white.transparent(0.12), + light: Color.black.transparent(0.12), + hc: focusBorder +}, localize('notebook.focusedRowBorder', "The color of the row's top and bottom border when the row is focused.")); + registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { const checkboxBackgroundColor = theme.getColor(settingsCheckboxBackground); if (checkboxBackgroundColor) { @@ -527,7 +546,7 @@ export class ListSettingWidget extends AbstractListSettingWidget valueInput.element.classList.add('setting-list-valueInput'); this.listDisposables.add(attachInputBoxStyler(valueInput, this.themeService, { - inputBackground: settingsTextInputBackground, + inputBackground: settingsSelectBackground, inputForeground: settingsTextInputForeground, inputBorder: settingsTextInputBorder })); @@ -546,7 +565,7 @@ export class ListSettingWidget extends AbstractListSettingWidget siblingInput.element.classList.add('setting-list-siblingInput'); this.listDisposables.add(siblingInput); this.listDisposables.add(attachInputBoxStyler(siblingInput, this.themeService, { - inputBackground: settingsTextInputBackground, + inputBackground: settingsSelectBackground, inputForeground: settingsTextInputForeground, inputBorder: settingsTextInputBorder })); @@ -908,7 +927,7 @@ export class ObjectSettingWidget extends AbstractListSettingWidget Date: Mon, 17 Aug 2020 09:07:33 +0200 Subject: [PATCH 264/736] add some more test for clarification --- src/vs/base/test/browser/dom.test.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/vs/base/test/browser/dom.test.ts b/src/vs/base/test/browser/dom.test.ts index 61252159d23..f5c8e65dac6 100644 --- a/src/vs/base/test/browser/dom.test.ts +++ b/src/vs/base/test/browser/dom.test.ts @@ -93,6 +93,22 @@ suite('dom', () => { assert(!div.firstChild); }); + test('should buld nodes with id', () => { + const div = $('div#foo'); + assert(div); + assert(div instanceof HTMLElement); + assert.equal(div.tagName, 'DIV'); + assert.equal(div.id, 'foo'); + }); + + test('should buld nodes with class-name', () => { + const div = $('div.foo'); + assert(div); + assert(div instanceof HTMLElement); + assert.equal(div.tagName, 'DIV'); + assert.equal(div.className, 'foo'); + }); + test('should build nodes with attributes', () => { let div = $('div', { class: 'test' }); assert.equal(div.className, 'test'); @@ -111,5 +127,12 @@ suite('dom', () => { assert.equal(div.firstChild && div.firstChild.textContent, 'hello'); }); + + test('should build nodes with text children', () => { + let div = $('div', undefined, 'foobar'); + let firstChild = div.firstChild as HTMLElement; + assert.equal(firstChild.tagName, undefined); + assert.equal(firstChild.textContent, 'foobar'); + }); }); }); From b7f7c2e4f00c549eb7783edcb04937c71dc1ad0b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 17 Aug 2020 09:15:24 +0200 Subject: [PATCH 265/736] dom - fix selector regex escaping --- src/vs/base/browser/dom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index f203119f8f6..e8b338ffb51 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -1004,7 +1004,7 @@ export function prepend(parent: HTMLElement, child: T): T { return child; } -const SELECTOR_REGEX = /([\w\-]+)?(#([\w\-]+))?((.([\w\-]+))*)/; +const SELECTOR_REGEX = /([\w\-]+)?(#([\w\-]+))?((\.([\w\-]+))*)/; export enum Namespace { HTML = 'http://www.w3.org/1999/xhtml', From 58720cf9e9dab3d9377fedbfe4decde92ccc4992 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 17 Aug 2020 09:27:52 +0200 Subject: [PATCH 266/736] editorservice - use async --- src/vs/workbench/services/editor/browser/editorService.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 88800a2c196..2f64b62a888 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -747,9 +747,9 @@ export class EditorService extends Disposable implements EditorServiceImpl { //#region replaceEditors() - replaceEditors(editors: IResourceEditorReplacement[], group: IEditorGroup | GroupIdentifier): Promise; - replaceEditors(editors: IEditorReplacement[], group: IEditorGroup | GroupIdentifier): Promise; - replaceEditors(editors: Array, group: IEditorGroup | GroupIdentifier): Promise { + async replaceEditors(editors: IResourceEditorReplacement[], group: IEditorGroup | GroupIdentifier): Promise; + async replaceEditors(editors: IEditorReplacement[], group: IEditorGroup | GroupIdentifier): Promise; + async replaceEditors(editors: Array, group: IEditorGroup | GroupIdentifier): Promise { const typedEditors: IEditorReplacement[] = []; editors.forEach(replaceEditorArg => { @@ -776,8 +776,6 @@ export class EditorService extends Disposable implements EditorServiceImpl { if (targetGroup) { return targetGroup.replaceEditors(typedEditors); } - - return Promise.resolve(); } //#endregion From cf94bd15cbd9988c0c23179c171457f28147fb30 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 17 Aug 2020 10:08:34 +0200 Subject: [PATCH 267/736] Move where TASKS_CATEGORY is defined --- src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts | 3 +-- src/vs/workbench/contrib/tasks/browser/task.contribution.ts | 3 +-- src/vs/workbench/contrib/tasks/common/tasks.ts | 1 + 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts b/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts index 0bbcafa4ff3..c17c12d9b76 100644 --- a/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts +++ b/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts @@ -8,12 +8,11 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { ITaskService, WorkspaceFolderTaskResult } from 'vs/workbench/contrib/tasks/common/taskService'; import { forEach } from 'vs/base/common/collections'; -import { RunOnOptions, Task, TaskRunSource } from 'vs/workbench/contrib/tasks/common/tasks'; +import { RunOnOptions, Task, TaskRunSource, TASKS_CATEGORY } from 'vs/workbench/contrib/tasks/common/tasks'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IQuickPickItem, IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { Action2 } from 'vs/platform/actions/common/actions'; -import { TASKS_CATEGORY } from 'vs/workbench/contrib/tasks/browser/task.contribution'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; const ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE = 'tasks.run.allowAutomatic'; diff --git a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts index e35b7641f36..25e5462bf69 100644 --- a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts @@ -20,7 +20,7 @@ import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor, IStatus import { IOutputChannelRegistry, Extensions as OutputExt } from 'vs/workbench/services/output/common/output'; -import { TaskEvent, TaskEventKind, TaskGroup, TASK_RUNNING_STATE } from 'vs/workbench/contrib/tasks/common/tasks'; +import { TaskEvent, TaskEventKind, TaskGroup, TASKS_CATEGORY, TASK_RUNNING_STATE } from 'vs/workbench/contrib/tasks/common/tasks'; import { ITaskService, ProcessExecutionSupportedContext, ShellExecutionSupportedContext } from 'vs/workbench/contrib/tasks/common/taskService'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -37,7 +37,6 @@ import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/pl import { TasksQuickAccessProvider } from 'vs/workbench/contrib/tasks/browser/tasksQuickAccess'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -export const TASKS_CATEGORY = { value: nls.localize('tasksCategory', "Tasks"), original: 'Tasks' }; const SHOW_TASKS_COMMANDS_CONTEXT = ContextKeyExpr.or(ShellExecutionSupportedContext, ProcessExecutionSupportedContext); const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); diff --git a/src/vs/workbench/contrib/tasks/common/tasks.ts b/src/vs/workbench/contrib/tasks/common/tasks.ts index ec40a24a548..7ec7668a9f6 100644 --- a/src/vs/workbench/contrib/tasks/common/tasks.ts +++ b/src/vs/workbench/contrib/tasks/common/tasks.ts @@ -19,6 +19,7 @@ import { ConfigurationTarget } from 'vs/platform/configuration/common/configurat import { USER_TASKS_GROUP_KEY } from 'vs/workbench/contrib/tasks/common/taskService'; export const TASK_RUNNING_STATE = new RawContextKey('taskRunning', false); +export const TASKS_CATEGORY = { value: nls.localize('tasksCategory', "Tasks"), original: 'Tasks' }; export enum ShellQuoting { /** From 442e27637de2ec4b24337299e49a1bb17071b9f5 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 17 Aug 2020 10:53:57 +0200 Subject: [PATCH 268/736] clean up for https://github.com/microsoft/vscode/issues/101282 --- src/vs/workbench/api/common/extHost.api.impl.ts | 2 +- src/vs/workbench/api/common/extHostNotebook.ts | 14 ++++---------- .../test/browser/api/extHostNotebook.test.ts | 9 ++++++++- .../api/extHostNotebookConcatDocument.test.ts | 10 ++++++++-- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 667fa4c6fa9..2d2a16538e9 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -137,7 +137,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostComment = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostCommands, extHostDocuments)); const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress))); const extHostLabelService = rpcProtocol.set(ExtHostContext.ExtHosLabelService, new ExtHostLabelService(rpcProtocol)); - const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, initData.uiKind === UIKind.Web ? new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, initData.environment, extHostLogService) : new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, initData.environment, extHostLogService, extensionStoragePaths)); + const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, initData.environment, extHostLogService, extensionStoragePaths)); const extHostTheming = rpcProtocol.set(ExtHostContext.ExtHostTheming, new ExtHostTheming(rpcProtocol)); const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol)); const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol, extHostCommands)); diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 037b0c71e63..a3528ff00c9 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -934,7 +934,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN private _documentsAndEditors: ExtHostDocumentsAndEditors, private readonly _webviewInitData: WebviewInitData, private readonly logService: ILogService, - private readonly _extensionStoragePaths?: IExtensionStoragePaths, + private readonly _extensionStoragePaths: IExtensionStoragePaths, ) { this._proxy = mainContext.getProxy(MainContext.MainThreadNotebook); @@ -1175,11 +1175,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN return; } - let storageRoot: URI | undefined; - if (this._extensionStoragePaths) { - storageRoot = this._extensionStoragePaths.workspaceValue(provider.extension) ?? this._extensionStoragePaths.globalValue(provider.extension); - } - + const storageRoot = this._extensionStoragePaths.workspaceValue(provider.extension) ?? this._extensionStoragePaths.globalValue(provider.extension); let document = this._documents.get(revivedUri); if (!document) { @@ -1547,10 +1543,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN const revivedUri = URI.revive(modelData.uri); const viewType = modelData.viewType; const entry = this._notebookContentProviders.get(viewType); - let storageRoot: URI | undefined; - if (entry && this._extensionStoragePaths) { - storageRoot = this._extensionStoragePaths.workspaceValue(entry.extension) ?? this._extensionStoragePaths.globalValue(entry.extension); - } + const storageRoot = entry && (this._extensionStoragePaths.workspaceValue(entry.extension) ?? this._extensionStoragePaths.globalValue(entry.extension)); + if (!this._documents.has(revivedUri)) { const that = this; diff --git a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts index b8e9555996f..d088232e237 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts @@ -18,6 +18,8 @@ import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { isEqual } from 'vs/base/common/resources'; +import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; +import { generateUuid } from 'vs/base/common/uuid'; suite('NotebookCell#Document', function () { @@ -43,7 +45,12 @@ suite('NotebookCell#Document', function () { }); extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(rpcProtocol, new NullLogService()); extHostDocuments = new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors); - extHostNotebooks = new ExtHostNotebookController(rpcProtocol, new ExtHostCommands(rpcProtocol, new NullLogService()), extHostDocumentsAndEditors, { isExtensionDevelopmentDebug: false, webviewCspSource: '', webviewResourceRoot: '' }, new NullLogService()); + const extHostStoragePaths = new class extends mock() { + workspaceValue() { + return URI.from({ scheme: 'test', path: generateUuid() }); + } + }; + extHostNotebooks = new ExtHostNotebookController(rpcProtocol, new ExtHostCommands(rpcProtocol, new NullLogService()), extHostDocumentsAndEditors, { isExtensionDevelopmentDebug: false, webviewCspSource: '', webviewResourceRoot: '' }, new NullLogService(), extHostStoragePaths); let reg = extHostNotebooks.registerNotebookContentProvider(nullExtensionDescription, 'test', new class extends mock() { // async openNotebook() { } }); diff --git a/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts b/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts index 237c1a30e4a..d7326865a25 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts @@ -19,7 +19,8 @@ import * as vscode from 'vscode'; import { mock } from 'vs/workbench/test/common/workbenchTestServices'; import { MainContext, MainThreadCommandsShape, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; import { DisposableStore } from 'vs/base/common/lifecycle'; - +import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; +import { generateUuid } from 'vs/base/common/uuid'; suite('NotebookConcatDocument', function () { @@ -44,7 +45,12 @@ suite('NotebookConcatDocument', function () { }); extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(rpcProtocol, new NullLogService()); extHostDocuments = new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors); - extHostNotebooks = new ExtHostNotebookController(rpcProtocol, new ExtHostCommands(rpcProtocol, new NullLogService()), extHostDocumentsAndEditors, { isExtensionDevelopmentDebug: false, webviewCspSource: '', webviewResourceRoot: '' }, new NullLogService()); + const extHostStoragePaths = new class extends mock() { + workspaceValue() { + return URI.from({ scheme: 'test', path: generateUuid() }); + } + }; + extHostNotebooks = new ExtHostNotebookController(rpcProtocol, new ExtHostCommands(rpcProtocol, new NullLogService()), extHostDocumentsAndEditors, { isExtensionDevelopmentDebug: false, webviewCspSource: '', webviewResourceRoot: '' }, new NullLogService(), extHostStoragePaths); let reg = extHostNotebooks.registerNotebookContentProvider(nullExtensionDescription, 'test', new class extends mock() { // async openNotebook() { } }); From e9cf1507ae91cf98a9295785e090d000fd23d51a Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 17 Aug 2020 11:03:43 +0200 Subject: [PATCH 269/736] onTypeRename code polish --- src/vs/editor/contrib/rename/onTypeRename.ts | 79 ++++++++++---------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/src/vs/editor/contrib/rename/onTypeRename.ts b/src/vs/editor/contrib/rename/onTypeRename.ts index 51cc31bbefe..3f41ce09e94 100644 --- a/src/vs/editor/contrib/rename/onTypeRename.ts +++ b/src/vs/editor/contrib/rename/onTypeRename.ts @@ -24,7 +24,7 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { URI } from 'vs/base/common/uri'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/errors'; +import { isPromiseCanceledError, onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/errors'; import * as strings from 'vs/base/common/strings'; import { registerColor } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; @@ -74,36 +74,33 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr this._updateMirrors = this._register(new RunOnceScheduler(() => this._doUpdateMirrors(), 0)); this._register(this._editor.onDidChangeModel((e) => { - this.stopAll(); this.run(); })); this._register(this._editor.onDidChangeConfiguration((e) => { if (e.hasChanged(EditorOption.renameOnType)) { this._enabled = this._editor.getOption(EditorOption.renameOnType); - this.stopAll(); this.run(); } })); this._register(this._editor.onDidChangeCursorPosition((e) => { + if (!this._enabled || !this._editor.hasModel()) { + return; + } + // no regions, run if (this._currentDecorations.length === 0) { this.run(e.position); + return; } - // has cached regions, don't run - if (!this._editor.hasModel()) { - return; - } - if (this._currentDecorations.length === 0) { - return; - } + // has cached regions const model = this._editor.getModel(); - const currentRanges = this._currentDecorations.map(decId => model.getDecorationRange(decId)!); + const primaryRange = model.getDecorationRange(this._currentDecorations[0]); // just moving cursor around, don't run again - if (Range.containsPosition(currentRanges[0], e.position)) { + if (primaryRange && Range.containsPosition(primaryRange, e.position)) { return; } @@ -138,19 +135,15 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr } private _doUpdateMirrors(): void { - if (!this._editor.hasModel()) { - return; - } - if (this._currentDecorations.length === 0) { + if (!this._editor.hasModel() || this._currentDecorations.length === 0) { // nothing to do return; } const model = this._editor.getModel(); - const currentRanges = this._currentDecorations.map(decId => model.getDecorationRange(decId)!); - const referenceRange = currentRanges[0]; - if (referenceRange.startLineNumber !== referenceRange.endLineNumber) { + const referenceRange = model.getDecorationRange(this._currentDecorations[0]); + if (!referenceRange || referenceRange.startLineNumber !== referenceRange.endLineNumber) { return this.stopAll(); } @@ -160,8 +153,11 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr } let edits: IIdentifiedSingleEditOperation[] = []; - for (let i = 1, len = currentRanges.length; i < len; i++) { - const mirrorRange = currentRanges[i]; + for (let i = 1, len = this._currentDecorations.length; i < len; i++) { + const mirrorRange = model.getDecorationRange(this._currentDecorations[i]); + if (!mirrorRange) { + continue; + } if (mirrorRange.startLineNumber !== mirrorRange.endLineNumber) { edits.push({ range: mirrorRange, @@ -214,29 +210,29 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr stopAll(): void { this._visibleContextKey.set(false); this._currentDecorations = this._editor.deltaDecorations(this._currentDecorations, []); - } - - async run(position: Position | null = this._editor.getPosition(), force = false): Promise { - if (!position) { - return; - } - if (!this._enabled && !force) { - return; - } - if (!this._editor.hasModel()) { - return; - } - if (this._currentRequest) { this._currentRequest.cancel(); this._currentRequest = null; } + } + async run(position: Position | null = this._editor.getPosition(), force = false): Promise { const model = this._editor.getModel(); - - this._currentRequest = createCancelablePromise(token => getOnTypeRenameRanges(model, position, token)); + if (!this._enabled && !force || !model || !position) { + this.stopAll(); + return; + } + const request = createCancelablePromise(token => getOnTypeRenameRanges(model, position, token)); + if (this._currentRequest) { + this._currentRequest.cancel(); + } + this._currentRequest = request; try { - const response = await this._currentRequest; + const response = await request; + if (request !== this._currentRequest) { + return; + } + this._currentRequest = null; let ranges: IRange[] = []; if (response?.ranges) { @@ -269,8 +265,13 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr this._visibleContextKey.set(true); this._currentDecorations = this._editor.deltaDecorations(this._currentDecorations, decorations); } catch (err) { - onUnexpectedError(err); - this.stopAll(); + if (!isPromiseCanceledError(err)) { + onUnexpectedError(err); + } + if (this._currentRequest === request || !this._currentRequest) { + // stop if we are still the latest request + this.stopAll(); + } } } } From 882cfecb3943ee50cb58e9a36ef16c98fb66c57d Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 17 Aug 2020 11:04:24 +0200 Subject: [PATCH 270/736] HTML Tag Syncing Not Breaking on Space. Fixes #103671 --- src/vs/editor/contrib/rename/onTypeRename.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/rename/onTypeRename.ts b/src/vs/editor/contrib/rename/onTypeRename.ts index 3f41ce09e94..d13cec4bf09 100644 --- a/src/vs/editor/contrib/rename/onTypeRename.ts +++ b/src/vs/editor/contrib/rename/onTypeRename.ts @@ -69,7 +69,7 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr this._visibleContextKey = CONTEXT_ONTYPE_RENAME_INPUT_VISIBLE.bindTo(contextKeyService); this._currentRequest = null; this._currentDecorations = []; - this._stopPattern = /^\s/; + this._stopPattern = /\s/; this._ignoreChangeEvent = false; this._updateMirrors = this._register(new RunOnceScheduler(() => this._doUpdateMirrors(), 0)); @@ -126,9 +126,11 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr if (e.isUndoing || e.isRedoing) { return; } - if (e.changes[0] && this._stopPattern.test(e.changes[0].text)) { - this.stopAll(); - return; + for (const change of e.changes) { + if (this._stopPattern.test(change.text)) { + this.stopAll(); + return; + } } this._updateMirrors.schedule(); })); From 9ee3142777b8880e4c2d3ad9ce06d96c06548fcf Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 17 Aug 2020 11:24:38 +0200 Subject: [PATCH 271/736] Tasks from Run Build Task and Run Test Task should be recently used part of #104714 --- .../contrib/tasks/browser/abstractTaskService.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index e757824eb43..888568abf02 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -2606,7 +2606,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } if (buildTasks.length === 1) { this.tryResolveTask(buildTasks[0]).then(resolvedTask => { - this.run(resolvedTask).then(undefined, reason => { + this.run(resolvedTask, undefined, TaskRunSource.User).then(undefined, reason => { // eat the error, it has already been surfaced to the user and we don't care about it here }); }); @@ -2617,7 +2617,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (tasks.length > 0) { let { defaults, users } = this.splitPerGroupType(tasks); if (defaults.length === 1) { - this.run(defaults[0]).then(undefined, reason => { + this.run(defaults[0], undefined, TaskRunSource.User).then(undefined, reason => { // eat the error, it has already been surfaced to the user and we don't care about it here }); return; @@ -2641,7 +2641,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this.runConfigureDefaultBuildTask(); return; } - this.run(task, { attachProblemMatcher: true }).then(undefined, reason => { + this.run(task, { attachProblemMatcher: true }, TaskRunSource.User).then(undefined, reason => { // eat the error, it has already been surfaced to the user and we don't care about it here }); }); @@ -2667,7 +2667,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (tasks.length > 0) { let { defaults, users } = this.splitPerGroupType(tasks); if (defaults.length === 1) { - this.run(defaults[0]).then(undefined, reason => { + this.run(defaults[0], undefined, TaskRunSource.User).then(undefined, reason => { // eat the error, it has already been surfaced to the user and we don't care about it here }); return; @@ -2691,7 +2691,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this.runConfigureTasks(); return; } - this.run(task).then(undefined, reason => { + this.run(task, undefined, TaskRunSource.User).then(undefined, reason => { // eat the error, it has already been surfaced to the user and we don't care about it here }); }); From 43ceb7258258ff20eaf81cf40be1153384e496d6 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 17 Aug 2020 11:49:18 +0200 Subject: [PATCH 272/736] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e40c9e33ec3..170869071d0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.49.0", - "distro": "af085a7e151908a14d22a08a22374f924b8a593d", + "distro": "0f12d5345f5e6b3586ae05fba75ad377fb8aafc1", "author": { "name": "Microsoft Corporation" }, From 652a432f59f54733bf17b79c9f2b7a925971a53d Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 17 Aug 2020 11:51:59 +0200 Subject: [PATCH 273/736] Set suite name based on the --web parameter (#104798) --- test/smoke/test/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/smoke/test/index.js b/test/smoke/test/index.js index 5e33b701fa3..76afb634fbc 100644 --- a/test/smoke/test/index.js +++ b/test/smoke/test/index.js @@ -7,13 +7,14 @@ const path = require('path'); const Mocha = require('mocha'); const minimist = require('minimist'); -const suite = 'Smoke Tests'; - const [, , ...args] = process.argv; const opts = minimist(args, { + boolean: 'web', string: ['f', 'g'] }); +const suite = opts['web'] ? 'Browser Smoke Tests' : 'Smoke Tests'; + const options = { color: true, timeout: 60000, From c233bf87bcf1bf1794a6aae62b01a09506b67d72 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Mon, 17 Aug 2020 12:28:51 +0200 Subject: [PATCH 274/736] add API to access DAP breakpoints; see #99716 --- src/vs/vscode.proposed.d.ts | 11 +++++++ .../api/browser/mainThreadDebugService.ts | 8 +++++ .../workbench/api/common/extHost.protocol.ts | 1 + .../api/common/extHostDebugService.ts | 4 +++ .../contrib/debug/browser/debugSession.ts | 4 +++ .../workbench/contrib/debug/common/debug.ts | 1 + .../contrib/debug/common/debugModel.ts | 29 +++++++++++++++++++ .../contrib/debug/test/common/mockDebug.ts | 3 ++ 8 files changed, 61 insertions(+) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 10e4324721b..843913db162 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -752,6 +752,17 @@ declare module 'vscode' { compact?: boolean; } + /** + * A DebugProtocolBreakpoint is an opaque stand-in type for the [Breakpoint](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Breakpoint) type defined in the Debug Adapter Protocol. + */ + export interface DebugProtocolBreakpoint { + // Properties: see details [here](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Breakpoint). + } + + export interface DebugSession { + getDebugProtocolBreakpoint(breakpoint: Breakpoint): DebugProtocolBreakpoint | undefined; + } + // deprecated debug API export interface DebugConfigurationProvider { diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts index 907f2886311..a723b1a38f1 100644 --- a/src/vs/workbench/api/browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts @@ -264,6 +264,14 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb return Promise.reject(new Error('debug session not found')); } + public $getDebugProtocolBreakpoint(sessionId: DebugSessionUUID, breakpoinId: string): Promise { + const session = this.debugService.getModel().getSession(sessionId, true); + if (session) { + return Promise.resolve(session.getDebugProtocolBreakpoint(breakpoinId)); + } + return Promise.reject(new Error('debug session not found')); + } + public $stopDebugging(sessionId: DebugSessionUUID | undefined): Promise { if (sessionId) { const session = this.debugService.getModel().getSession(sessionId, true); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index fcee75d3127..512dbb8d5bc 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -879,6 +879,7 @@ export interface MainThreadDebugServiceShape extends IDisposable { $stopDebugging(sessionId: DebugSessionUUID | undefined): Promise; $setDebugSessionName(id: DebugSessionUUID, name: string): void; $customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): Promise; + $getDebugProtocolBreakpoint(id: DebugSessionUUID, breakpoinId: string): Promise; $appendDebugConsole(value: string): void; $startBreakpointEvents(): void; $registerBreakpoints(breakpoints: Array): Promise; diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index 82121bcb2c6..1e6b3429aa4 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -957,6 +957,10 @@ export class ExtHostDebugSession implements vscode.DebugSession { public customRequest(command: string, args: any): Promise { return this._debugServiceProxy.$customDebugAdapterRequest(this._id, command, args); } + + public getDebugProtocolBreakpoint(breakpoint: vscode.Breakpoint): vscode.DebugProtocolBreakpoint | undefined { + return this._debugServiceProxy.$getDebugProtocolBreakpoint(this._id, breakpoint.id); + } } export class ExtHostDebugConsole implements vscode.DebugConsole { diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 19c471f74bd..22373d30863 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -440,6 +440,10 @@ export class DebugSession implements IDebugSession { return distinct(positions, p => `${p.lineNumber}:${p.column}`); } + getDebugProtocolBreakpoint(breakpointId: string): DebugProtocol.Breakpoint | undefined { + return this.model.getDebugProtocolBreakpoint(breakpointId, this.getId()); + } + customRequest(request: string, args: any): Promise { if (!this.raw) { throw new Error(localize('noDebugAdapter', "No debug adapter, can not send '{0}'", request)); diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 8353ae1ab17..885b800c4ae 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -224,6 +224,7 @@ export interface IDebugSession extends ITreeElement { sendDataBreakpoints(dbps: IDataBreakpoint[]): Promise; sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise; breakpointsLocations(uri: uri, lineNumber: number): Promise; + getDebugProtocolBreakpoint(breakpointId: string): DebugProtocol.Breakpoint | undefined; stackTrace(threadId: number, startFrame: number, levels: number, token: CancellationToken): Promise; exceptionInfo(threadId: number): Promise; diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 0a5620c8d4b..80060824d0d 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -597,6 +597,27 @@ export abstract class BaseBreakpoint extends Enablement implements IBaseBreakpoi return data ? data.id : undefined; } + getDebugProtocolBreakpoint(sessionId: string): DebugProtocol.Breakpoint | undefined { + const data = this.sessionData.get(sessionId); + if (data) { + const bp: DebugProtocol.Breakpoint = { + id: data.id, + verified: data.verified, + message: data.message, + source: data.source, + line: data.line, + column: data.column, + endLine: data.endLine, + endColumn: data.endColumn, + instructionReference: data.instructionReference, + offset: data.offset + // TODO: copy additional debug adapter data + }; + return bp; + } + return undefined; + } + toJSON(): any { const result = Object.create(null); result.enabled = this.enabled; @@ -1094,6 +1115,14 @@ export class DebugModel implements IDebugModel { }); } + getDebugProtocolBreakpoint(breakpointId: string, sessionId: string): DebugProtocol.Breakpoint | undefined { + const bp = this.breakpoints.find(bp => bp.getId()); + if (bp) { + return bp.getDebugProtocolBreakpoint(sessionId); + } + return undefined; + } + private sortAndDeDup(): void { this.breakpoints = this.breakpoints.sort((first, second) => { if (first.uri.toString() !== second.uri.toString()) { diff --git a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts index f3057da0e95..9f4002da8b9 100644 --- a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts +++ b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts @@ -293,6 +293,9 @@ export class MockSession implements IDebugSession { sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise { throw new Error('Method not implemented.'); } + getDebugProtocolBreakpoint(breakpointId: string): DebugProtocol.Breakpoint | undefined { + throw new Error('Method not implemented.'); + } customRequest(request: string, args: any): Promise { throw new Error('Method not implemented.'); } From 6344d2f62cedb2e5574888fb14c144839da4b25e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 17 Aug 2020 14:20:29 +0200 Subject: [PATCH 275/736] Fix #104652 --- src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 049be0714d3..56ab09a0ba8 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -326,7 +326,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo if (isEqual(this.userDataSyncStoreManagementService.userDataSyncStore?.url, this.userDataSyncStoreManagementService.userDataSyncStore?.insidersUrl)) { this.notificationService.notify({ severity: Severity.Info, - message: localize('switched to insiders', "Settings sync now uses a separate service, more information is available in the [release notes](command:update.showCurrentReleaseNotes)."), + message: localize('switched to insiders', "Settings sync now uses a separate service, more information is available in the [release notes](https://code.visualstudio.com/updates/v1_48#_settings-sync)."), }); } return; From 04f2a740c7ea3eab0b2c8de1805145bd69be8dff Mon Sep 17 00:00:00 2001 From: John Murray Date: Mon, 17 Aug 2020 14:12:26 +0100 Subject: [PATCH 276/736] fix #104698 Make query element of uri survive conflict resolution massaging (#104699) * fix #104698 Make query element of uri survive conflict resolution massaging * Incorporate PR feedback * Simplify * Event simpler, and with comments * Variant preferred by @bpasero --- src/vs/workbench/contrib/files/common/files.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/files/common/files.ts b/src/vs/workbench/contrib/files/common/files.ts index 3fe4953a5c6..a6ec9ed423f 100644 --- a/src/vs/workbench/contrib/files/common/files.ts +++ b/src/vs/workbench/contrib/files/common/files.ts @@ -164,11 +164,12 @@ export class TextFileContentProvider extends Disposable implements ITextModelCon } private static resourceToTextFile(scheme: string, resource: URI): URI { - return resource.with({ scheme, query: JSON.stringify({ scheme: resource.scheme }) }); + return resource.with({ scheme, query: JSON.stringify({ scheme: resource.scheme, query: resource.query }) }); } private static textFileToResource(resource: URI): URI { - return resource.with({ scheme: JSON.parse(resource.query)['scheme'], query: null }); + const { scheme, query } = JSON.parse(resource.query); + return resource.with({ scheme, query }); } async provideTextContent(resource: URI): Promise { From 37b249446272eb23824641949e0a7461ddb266c0 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 17 Aug 2020 15:15:03 +0200 Subject: [PATCH 277/736] chore - mark removeNode as deprecated --- src/vs/base/browser/dom.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index e8b338ffb51..29490d6fc1e 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -23,6 +23,9 @@ export function clearNode(node: HTMLElement): void { } } +/** + * @deprecated use `node.remove()` instead + */ export function removeNode(node: HTMLElement): void { if (node.parentNode) { node.parentNode.removeChild(node); From d178b14151ded5dc0f084e938a6791a2a847ca87 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 17 Aug 2020 15:48:21 +0200 Subject: [PATCH 278/736] debug: getDebugProtocolBreakpoint: make sure to use the breakpoint id #99716 --- src/vs/workbench/contrib/debug/common/debugModel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 80060824d0d..efa503f6f06 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -1116,7 +1116,7 @@ export class DebugModel implements IDebugModel { } getDebugProtocolBreakpoint(breakpointId: string, sessionId: string): DebugProtocol.Breakpoint | undefined { - const bp = this.breakpoints.find(bp => bp.getId()); + const bp = this.breakpoints.find(bp => bp.getId() === breakpointId); if (bp) { return bp.getDebugProtocolBreakpoint(sessionId); } From 908af6a089c23c61c21dcb3094ef801f76e29ef4 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 17 Aug 2020 15:54:17 +0200 Subject: [PATCH 279/736] remove todo --- src/vs/workbench/contrib/debug/common/debugModel.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index efa503f6f06..1d302de30e3 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -611,7 +611,6 @@ export abstract class BaseBreakpoint extends Enablement implements IBaseBreakpoi endColumn: data.endColumn, instructionReference: data.instructionReference, offset: data.offset - // TODO: copy additional debug adapter data }; return bp; } From c8f9d00bb85129b415fd955bcb7d6f0b9c463506 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru Date: Mon, 17 Aug 2020 16:34:00 +0200 Subject: [PATCH 280/736] Consistent order of running tests --- .../darwin/product-build-darwin.yml | 14 +++++++------- .../azure-pipelines/linux/product-build-linux.yml | 14 +++++++------- .../azure-pipelines/win32/product-build-win32.yml | 14 +++++++------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 58353f1f283..3b186bb1136 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -118,6 +118,13 @@ steps: displayName: Run integration tests (Electron) condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) +- script: | + set -e + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin" \ + ./resources/server/test/test-web-integration.sh --browser webkit + displayName: Run integration tests (Browser) + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | set -e APP_ROOT=$(agent.builddirectory)/VSCode-darwin @@ -128,13 +135,6 @@ steps: displayName: Run remote integration tests (Electron) condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - set -e - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin" \ - ./resources/server/test/test-web-integration.sh --browser webkit - displayName: Run integration tests (Browser) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - script: | set -e APP_ROOT=$(agent.builddirectory)/VSCode-darwin diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index ea6c0195954..21d963042c8 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -123,6 +123,13 @@ steps: displayName: Run integration tests (Electron) condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) +- script: | + set -e + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-x64" \ + DISPLAY=:10 ./resources/server/test/test-web-integration.sh --browser chromium + displayName: Run integration tests (Browser) + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | set -e APP_ROOT=$(agent.builddirectory)/VSCode-linux-x64 @@ -133,13 +140,6 @@ steps: displayName: Run remote integration tests (Electron) condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - set -e - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-x64" \ - DISPLAY=:10 ./resources/server/test/test-web-integration.sh --browser chromium - displayName: Run integration tests (Browser) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - task: PublishPipelineArtifact@0 inputs: artifactName: crash-dump-linux diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 779bc8a8d57..be80731a7ab 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -135,18 +135,18 @@ steps: - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" - $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json - $AppNameShort = $AppProductJson.nameShort - exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\resources\server\test\test-remote-integration.bat } - displayName: Run remote integration tests (Electron) + exec { $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"; .\resources\server\test\test-web-integration.bat --browser firefox } + displayName: Run integration tests (Browser) condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - exec { $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"; .\resources\server\test\test-web-integration.bat --browser firefox } - displayName: Run integration tests (Browser) + $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" + $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json + $AppNameShort = $AppProductJson.nameShort + exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\resources\server\test\test-remote-integration.bat } + displayName: Run remote integration tests (Electron) condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - task: PublishPipelineArtifact@0 From 185bf255ad6e5e26c293725846dc6608db86a42a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 17 Aug 2020 17:05:30 +0200 Subject: [PATCH 281/736] Revert "fix #104698 Make query element of uri survive conflict resolution massaging (#104699)" This reverts commit 04f2a740c7ea3eab0b2c8de1805145bd69be8dff. --- src/vs/workbench/contrib/files/common/files.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/files/common/files.ts b/src/vs/workbench/contrib/files/common/files.ts index a6ec9ed423f..3fe4953a5c6 100644 --- a/src/vs/workbench/contrib/files/common/files.ts +++ b/src/vs/workbench/contrib/files/common/files.ts @@ -164,12 +164,11 @@ export class TextFileContentProvider extends Disposable implements ITextModelCon } private static resourceToTextFile(scheme: string, resource: URI): URI { - return resource.with({ scheme, query: JSON.stringify({ scheme: resource.scheme, query: resource.query }) }); + return resource.with({ scheme, query: JSON.stringify({ scheme: resource.scheme }) }); } private static textFileToResource(resource: URI): URI { - const { scheme, query } = JSON.parse(resource.query); - return resource.with({ scheme, query }); + return resource.with({ scheme: JSON.parse(resource.query)['scheme'], query: null }); } async provideTextContent(resource: URI): Promise { From 11dc5a81ba248cc2678888391f1b24dccabddaf8 Mon Sep 17 00:00:00 2001 From: Robo Date: Mon, 17 Aug 2020 09:40:44 -0700 Subject: [PATCH 282/736] build: include auth dialog resource (#104841) --- build/gulpfile.vscode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 1bf7d36a9f6..3eb2cfdc4fe 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -79,7 +79,7 @@ const vscodeResources = [ 'out-build/vs/code/electron-browser/sharedProcess/sharedProcess.js', 'out-build/vs/code/electron-sandbox/issue/issueReporter.js', 'out-build/vs/code/electron-sandbox/processExplorer/processExplorer.js', - 'out-build/vs/platform/auth/common/auth.css', + 'out-build/vs/code/electron-sandbox/proxy/auth.js', '!**/test/**' ]; From 386fc8c799bc2da6bec58a1ba844d181fa181b46 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Mon, 17 Aug 2020 10:19:42 -0700 Subject: [PATCH 283/736] Update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 170869071d0..9785d31d5a5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.49.0", - "distro": "0f12d5345f5e6b3586ae05fba75ad377fb8aafc1", + "distro": "1af19f18e9d2676f5ab14a56d5c94ad418c3eb80", "author": { "name": "Microsoft Corporation" }, From 05e0e027515f36c56081feffbf1f237d452e35eb Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 17 Aug 2020 11:54:44 -0700 Subject: [PATCH 284/736] Fix toolbar only showing up for hovered rows, not focused rows Fix #104229 --- src/vs/workbench/contrib/notebook/browser/media/notebook.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 6d3c9fbc887..ffd4ca82fa9 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -438,7 +438,7 @@ height: 2px; } -.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-has-toolbar-actions.focused .cell-title-toolbar, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused .cell-has-toolbar-actions .cell-title-toolbar, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-has-toolbar-actions.cell-output-hover .cell-title-toolbar, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-has-toolbar-actions:hover .cell-title-toolbar { visibility: visible; From d28477e20fa1c0edbd50210def4ea5ccf1e6c318 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 17 Aug 2020 14:27:24 -0700 Subject: [PATCH 285/736] Force horizontalScrolling=off for SettingsTree Fix #104843 --- src/vs/workbench/contrib/preferences/browser/settingsTree.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index eb3a268f054..df568849718 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -1915,6 +1915,7 @@ export class SettingsTree extends WorkbenchObjectTree { new SettingsTreeDelegate(), renderers, { + horizontalScrolling: false, supportDynamicHeights: true, identityProvider: { getId(e) { From 4cee5da307c610875345932e20728ebd2b1a4486 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 17 Aug 2020 23:29:13 +0200 Subject: [PATCH 286/736] Fix #103839 --- .../userDataSync/common/keybindingsMerge.ts | 14 ++-- .../userDataSync/common/keybindingsSync.ts | 2 +- .../test/common/keybindingsSync.test.ts | 64 +++++++++++++++++++ 3 files changed, 74 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/userDataSync/common/keybindingsMerge.ts b/src/vs/platform/userDataSync/common/keybindingsMerge.ts index 3f994050bc7..3cbd0faf84d 100644 --- a/src/vs/platform/userDataSync/common/keybindingsMerge.ts +++ b/src/vs/platform/userDataSync/common/keybindingsMerge.ts @@ -28,10 +28,14 @@ interface IMergeResult { conflicts: Set; } +export function parseKeybindings(content: string): IUserFriendlyKeybinding[] { + return parse(content) || []; +} + export async function merge(localContent: string, remoteContent: string, baseContent: string | null, formattingOptions: FormattingOptions, userDataSyncUtilService: IUserDataSyncUtilService): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }> { - const local = parse(localContent); - const remote = parse(remoteContent); - const base = baseContent ? parse(baseContent) : null; + const local = parseKeybindings(localContent); + const remote = parseKeybindings(remoteContent); + const base = baseContent ? parseKeybindings(baseContent) : null; const userbindings: string[] = [...local, ...remote, ...(base || [])].map(keybinding => keybinding.key); const normalizedKeys = await userDataSyncUtilService.resolveUserBindings(userbindings); @@ -331,7 +335,7 @@ function addKeybindings(content: string, keybindings: IUserFriendlyKeybinding[], } function removeKeybindings(content: string, command: string, formattingOptions: FormattingOptions): string { - const keybindings = parse(content); + const keybindings = parseKeybindings(content); for (let index = keybindings.length - 1; index >= 0; index--) { if (keybindings[index].command === command || keybindings[index].command === `-${command}`) { content = contentUtil.edit(content, [index], undefined, formattingOptions); @@ -341,7 +345,7 @@ function removeKeybindings(content: string, command: string, formattingOptions: } function updateKeybindings(content: string, command: string, keybindings: IUserFriendlyKeybinding[], formattingOptions: FormattingOptions): string { - const allKeybindings = parse(content); + const allKeybindings = parseKeybindings(content); const location = findFirstIndex(allKeybindings, keybinding => keybinding.command === command || keybinding.command === `-${command}`); // Remove all entries with this command for (let index = allKeybindings.length - 1; index >= 0; index--) { diff --git a/src/vs/platform/userDataSync/common/keybindingsSync.ts b/src/vs/platform/userDataSync/common/keybindingsSync.ts index cfafec8c3ae..696be2c843b 100644 --- a/src/vs/platform/userDataSync/common/keybindingsSync.ts +++ b/src/vs/platform/userDataSync/common/keybindingsSync.ts @@ -209,7 +209,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem if (lastSyncUserData?.ref !== remoteUserData.ref) { this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized keybindings...`); - const lastSyncContent = content !== null ? this.toSyncContent(content, null) : null; + const lastSyncContent = content !== null ? this.toSyncContent(content, null) : remoteUserData.syncData?.content; await this.updateLastSyncUserData({ ref: remoteUserData.ref, syncData: lastSyncContent ? { diff --git a/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts b/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts index 02b0154b627..d1a6ca7516b 100644 --- a/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts @@ -100,6 +100,70 @@ suite('KeybindingsSync', () => { assert.equal((await fileService.readFile(keybindingsResource)).value.toString(), content); }); + test('when keybindings file is empty with comment and remote has no changes', async () => { + const fileService = client.instantiationService.get(IFileService); + const keybindingsResource = client.instantiationService.get(IEnvironmentService).keybindingsResource; + const expectedContent = '// Empty Keybindings'; + await fileService.writeFile(keybindingsResource, VSBuffer.fromString(expectedContent)); + + await testObject.sync(await client.manifest()); + + const lastSyncUserData = await testObject.getLastSyncUserData(); + const remoteUserData = await testObject.getRemoteUserData(null); + assert.equal(testObject.getKeybindingsContentFromSyncContent(lastSyncUserData!.syncData!.content!), expectedContent); + assert.equal(testObject.getKeybindingsContentFromSyncContent(remoteUserData!.syncData!.content!), expectedContent); + assert.equal((await fileService.readFile(keybindingsResource)).value.toString(), expectedContent); + }); + + test('when keybindings file is empty and remote has keybindings', async () => { + const client2 = disposableStore.add(new UserDataSyncClient(server)); + await client2.setUp(true); + const content = JSON.stringify([ + { + 'key': 'shift+cmd+w', + 'command': 'workbench.action.closeAllEditors', + } + ]); + await client2.instantiationService.get(IFileService).writeFile(client2.instantiationService.get(IEnvironmentService).keybindingsResource, VSBuffer.fromString(content)); + await client2.sync(); + + const fileService = client.instantiationService.get(IFileService); + const keybindingsResource = client.instantiationService.get(IEnvironmentService).keybindingsResource; + await fileService.writeFile(keybindingsResource, VSBuffer.fromString('// Empty Keybindings')); + + await testObject.sync(await client.manifest()); + + const lastSyncUserData = await testObject.getLastSyncUserData(); + const remoteUserData = await testObject.getRemoteUserData(null); + assert.equal(testObject.getKeybindingsContentFromSyncContent(lastSyncUserData!.syncData!.content!), content); + assert.equal(testObject.getKeybindingsContentFromSyncContent(remoteUserData!.syncData!.content!), content); + assert.equal((await fileService.readFile(keybindingsResource)).value.toString(), content); + }); + + test('when keybindings file is empty and remote has empty array', async () => { + const client2 = disposableStore.add(new UserDataSyncClient(server)); + await client2.setUp(true); + const content = + `// Place your key bindings in this file to override the defaults +[ +]`; + await client2.instantiationService.get(IFileService).writeFile(client2.instantiationService.get(IEnvironmentService).keybindingsResource, VSBuffer.fromString(content)); + await client2.sync(); + + const fileService = client.instantiationService.get(IFileService); + const keybindingsResource = client.instantiationService.get(IEnvironmentService).keybindingsResource; + const expectedLocalContent = '// Empty Keybindings'; + await fileService.writeFile(keybindingsResource, VSBuffer.fromString(expectedLocalContent)); + + await testObject.sync(await client.manifest()); + + const lastSyncUserData = await testObject.getLastSyncUserData(); + const remoteUserData = await testObject.getRemoteUserData(null); + assert.equal(testObject.getKeybindingsContentFromSyncContent(lastSyncUserData!.syncData!.content!), content); + assert.equal(testObject.getKeybindingsContentFromSyncContent(remoteUserData!.syncData!.content!), content); + assert.equal((await fileService.readFile(keybindingsResource)).value.toString(), expectedLocalContent); + }); + test('when keybindings file is created after first sync', async () => { const fileService = client.instantiationService.get(IFileService); const keybindingsResource = client.instantiationService.get(IEnvironmentService).keybindingsResource; From 76bc88fd9d2e658c3ed3b46e3c47ab746a274d48 Mon Sep 17 00:00:00 2001 From: mtaran-google Date: Mon, 17 Aug 2020 14:38:32 -0700 Subject: [PATCH 287/736] Ban unreachable code. (#104521) This changes unreachable code from a warning presented in the editor to an error. Existing issues mostly revolved around switching on enums. These were fixed by converting the switches to use a default case, leaving the behavior unchanged in case of new values (e.g. from separately-compiled extensions) creeping in. Other cases had to do with the assert.fail() function, which throws and so shouldn't have anything following it. Co-authored-by: Maksym Taran --- src/tsconfig.base.json | 1 + src/vs/base/test/node/pfs/pfs.test.ts | 1 - .../editor/browser/controller/coreCommands.ts | 4 ++-- src/vs/editor/browser/view/viewController.ts | 6 ++++-- .../common/controller/cursorMoveCommands.ts | 7 ++++--- .../pieceTreeTextBuffer.ts | 3 ++- .../browser/inspectTokens/inspectTokens.ts | 2 +- .../severityIcon/common/severityIcon.ts | 3 ++- .../api/common/extHostTypeConverters.ts | 14 +++++++------ src/vs/workbench/browser/layout.ts | 4 ++-- .../inspectEditorTokens.ts | 2 +- .../debugANSIHandling.test.ts | 1 - .../emmet/test/browser/emmetAction.test.ts | 2 -- .../common/configurationEditingService.ts | 9 ++++++--- .../test/common/rpcProtocol.test.ts | 20 +++++-------------- .../services/layout/browser/layoutService.ts | 3 +-- .../test/browser/api/extHostTreeViews.test.ts | 3 ++- 17 files changed, 41 insertions(+), 44 deletions(-) diff --git a/src/tsconfig.base.json b/src/tsconfig.base.json index 19165d97b72..a4c419cf5f4 100644 --- a/src/tsconfig.base.json +++ b/src/tsconfig.base.json @@ -5,6 +5,7 @@ "experimentalDecorators": true, "noImplicitReturns": true, "noUnusedLocals": true, + "allowUnreachableCode": false, "strict": true, "forceConsistentCasingInFileNames": true, "baseUrl": ".", diff --git a/src/vs/base/test/node/pfs/pfs.test.ts b/src/vs/base/test/node/pfs/pfs.test.ts index c82436e3b82..fd324076b26 100644 --- a/src/vs/base/test/node/pfs/pfs.test.ts +++ b/src/vs/base/test/node/pfs/pfs.test.ts @@ -224,7 +224,6 @@ suite('PFS', function () { } catch (error) { assert.fail(error); - throw error; } }); diff --git a/src/vs/editor/browser/controller/coreCommands.ts b/src/vs/editor/browser/controller/coreCommands.ts index 2013ad47528..63e9488203a 100644 --- a/src/vs/editor/browser/controller/coreCommands.ts +++ b/src/vs/editor/browser/controller/coreCommands.ts @@ -559,9 +559,9 @@ export namespace CoreNavigationCommands { case CursorMove_.Direction.ViewPortCenter: case CursorMove_.Direction.ViewPortIfOutside: return CursorMoveCommands.viewportMove(viewModel, cursors, args.direction, inSelectionMode, value); + default: + return null; } - - return null; } } diff --git a/src/vs/editor/browser/view/viewController.ts b/src/vs/editor/browser/view/viewController.ts index 69880b0133d..519a26bb5f8 100644 --- a/src/vs/editor/browser/view/viewController.ts +++ b/src/vs/editor/browser/view/viewController.ts @@ -109,8 +109,9 @@ export class ViewController { return data.ctrlKey; case 'metaKey': return data.metaKey; + default: + return false; } - return false; } private _hasNonMulticursorModifier(data: IMouseDispatchData): boolean { @@ -121,8 +122,9 @@ export class ViewController { return data.altKey || data.metaKey; case 'metaKey': return data.ctrlKey || data.altKey; + default: + return false; } - return false; } public dispatchMouse(data: IMouseDispatchData): void { diff --git a/src/vs/editor/common/controller/cursorMoveCommands.ts b/src/vs/editor/common/controller/cursorMoveCommands.ts index 68d4e69ebf0..bc9bbb1c390 100644 --- a/src/vs/editor/common/controller/cursorMoveCommands.ts +++ b/src/vs/editor/common/controller/cursorMoveCommands.ts @@ -317,9 +317,10 @@ export class CursorMoveCommands { // Move to the last non-whitespace column of the current view line return this._moveToViewLastNonWhitespaceColumn(viewModel, cursors, inSelectionMode); } + default: + return null; } - return null; } public static viewportMove(viewModel: IViewModel, cursors: CursorState[], direction: CursorMove.ViewportDirection, inSelectionMode: boolean, value: number): PartialCursorState[] | null { @@ -353,9 +354,9 @@ export class CursorMoveCommands { } return result; } + default: + return null; } - - return null; } public static findPositionInViewportIfOutside(viewModel: IViewModel, cursor: CursorState, visibleViewRange: Range, inSelectionMode: boolean): PartialCursorState { diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts index 1c81c18a0a6..a3cf42be668 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts @@ -214,8 +214,9 @@ export class PieceTreeTextBuffer implements ITextBuffer, IDisposable { return '\r\n'; case EndOfLinePreference.TextDefined: return this.getEOL(); + default: + throw new Error('Unknown EOL preference'); } - throw new Error('Unknown EOL preference'); } public setEOL(newEOL: '\r\n' | '\n'): void { diff --git a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts index 08d629e4260..f219c6cafec 100644 --- a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts +++ b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts @@ -265,8 +265,8 @@ class InspectTokensWidget extends Disposable implements IContentWidget { case StandardTokenType.Comment: return 'Comment'; case StandardTokenType.String: return 'String'; case StandardTokenType.RegEx: return 'RegEx'; + default: return '??'; } - return '??'; } private _fontStyleToString(fontStyle: FontStyle): string { diff --git a/src/vs/platform/severityIcon/common/severityIcon.ts b/src/vs/platform/severityIcon/common/severityIcon.ts index f09bc4b255b..8b4697e84b9 100644 --- a/src/vs/platform/severityIcon/common/severityIcon.ts +++ b/src/vs/platform/severityIcon/common/severityIcon.ts @@ -20,8 +20,9 @@ export namespace SeverityIcon { return Codicon.warning.classNames; case Severity.Error: return Codicon.error.classNames; + default: + return ''; } - return ''; } } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 6a7993f9ae7..66b48dbacd8 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -131,8 +131,9 @@ export namespace DiagnosticTag { return types.DiagnosticTag.Unnecessary; case MarkerTag.Deprecated: return types.DiagnosticTag.Deprecated; + default: + return undefined; } - return undefined; } } @@ -210,8 +211,9 @@ export namespace DiagnosticSeverity { return types.DiagnosticSeverity.Error; case MarkerSeverity.Hint: return types.DiagnosticSeverity.Hint; + default: + return types.DiagnosticSeverity.Error; } - return types.DiagnosticSeverity.Error; } } @@ -1253,9 +1255,9 @@ export namespace LogLevel { return _MainLogLevel.Critical; case types.LogLevel.Off: return _MainLogLevel.Off; + default: + return _MainLogLevel.Info; } - - return _MainLogLevel.Info; } export function to(mainLevel: _MainLogLevel): types.LogLevel { @@ -1274,8 +1276,8 @@ export namespace LogLevel { return types.LogLevel.Critical; case _MainLogLevel.Off: return types.LogLevel.Off; + default: + return types.LogLevel.Info; } - - return types.LogLevel.Info; } } diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 2385e99eac7..dd1e2146344 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1061,9 +1061,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi return !this.state.activityBar.hidden; case Parts.EDITOR_PART: return !this.state.editor.hidden; + default: + return true; // any other part cannot be hidden } - - return true; // any other part cannot be hidden } focus(): void { diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts index 069b114a77a..d4ca7479c7f 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts @@ -425,8 +425,8 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget { case StandardTokenType.Comment: return 'Comment'; case StandardTokenType.String: return 'String'; case StandardTokenType.RegEx: return 'RegEx'; + default: return '??'; } - return '??'; } private _getTokensAtPosition(grammar: IGrammar, position: Position): ITextMateTokenInfo { diff --git a/src/vs/workbench/contrib/debug/test/electron-browser/debugANSIHandling.test.ts b/src/vs/workbench/contrib/debug/test/electron-browser/debugANSIHandling.test.ts index 9b504a180a9..abe90db10c1 100644 --- a/src/vs/workbench/contrib/debug/test/electron-browser/debugANSIHandling.test.ts +++ b/src/vs/workbench/contrib/debug/test/electron-browser/debugANSIHandling.test.ts @@ -88,7 +88,6 @@ suite('Debug - ANSI Handling', () => { return child; } else { assert.fail('Unexpected assertion error'); - return null!; } } diff --git a/src/vs/workbench/contrib/emmet/test/browser/emmetAction.test.ts b/src/vs/workbench/contrib/emmet/test/browser/emmetAction.test.ts index 048c43ef01a..c56afa2d157 100644 --- a/src/vs/workbench/contrib/emmet/test/browser/emmetAction.test.ts +++ b/src/vs/workbench/contrib/emmet/test/browser/emmetAction.test.ts @@ -56,14 +56,12 @@ suite('Emmet', () => { const model = editor.getModel(); if (!model) { assert.fail('Editor model not found'); - return; } model.setMode(languageIdentifier); let langOutput = EmmetEditorAction.getLanguage(languageIdentifierResolver, editor, new MockGrammarContributions(scopeName)); if (!langOutput) { assert.fail('langOutput not found'); - return; } assert.equal(langOutput.language, expectedLanguage); diff --git a/src/vs/workbench/services/configuration/common/configurationEditingService.ts b/src/vs/workbench/services/configuration/common/configurationEditingService.ts index 37ef711911b..d748536584c 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditingService.ts @@ -353,8 +353,9 @@ export class ConfigurationEditingService { } } return nls.localize('errorInvalidConfigurationFolder', "Unable to write into folder settings. Please open the '{0}' folder settings to correct errors/warnings in it and try again.", workspaceFolderName); + default: + return ''; } - return ''; } case ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_DIRTY: { if (operation.workspaceStandAloneConfigurationKey === TASKS_CONFIGURATION_KEY) { @@ -379,8 +380,9 @@ export class ConfigurationEditingService { } } return nls.localize('errorConfigurationFileDirtyFolder', "Unable to write into folder settings because the file is dirty. Please save the '{0}' folder settings file first and then try again.", workspaceFolderName); + default: + return ''; } - return ''; } case ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_MODIFIED_SINCE: if (operation.workspaceStandAloneConfigurationKey === TASKS_CONFIGURATION_KEY) { @@ -412,8 +414,9 @@ export class ConfigurationEditingService { return nls.localize('workspaceTarget', "Workspace Settings"); case EditableConfigurationTarget.WORKSPACE_FOLDER: return nls.localize('folderTarget', "Folder Settings"); + default: + return ''; } - return ''; } private getEdits(model: ITextModel, edit: IConfigurationEditOperation): Edit[] { diff --git a/src/vs/workbench/services/extensions/test/common/rpcProtocol.test.ts b/src/vs/workbench/services/extensions/test/common/rpcProtocol.test.ts index 637a617c76c..976d2b1cc19 100644 --- a/src/vs/workbench/services/extensions/test/common/rpcProtocol.test.ts +++ b/src/vs/workbench/services/extensions/test/common/rpcProtocol.test.ts @@ -139,11 +139,9 @@ suite('RPCProtocol', () => { let p = bProxy.$m(4, tokenSource.token); p.then((res: number) => { assert.equal(res, 7); - done(null); }, (err) => { assert.fail('should not receive error'); - done(); - }); + }).finally(done); tokenSource.cancel(); }); @@ -153,11 +151,9 @@ suite('RPCProtocol', () => { }; bProxy.$m(4, 1).then((res) => { assert.fail('unexpected'); - done(null); }, (err) => { assert.equal(err.message, 'nope'); - done(null); - }); + }).finally(done); }); test('error promise', function (done) { @@ -166,11 +162,9 @@ suite('RPCProtocol', () => { }; bProxy.$m(4, 1).then((res) => { assert.fail('unexpected'); - done(null); }, (err) => { assert.equal(err, undefined); - done(null); - }); + }).finally(done); }); test('issue #60450: Converting circular structure to JSON', function (done) { @@ -181,11 +175,9 @@ suite('RPCProtocol', () => { }; bProxy.$m(4, 1).then((res) => { assert.equal(res, null); - done(null); }, (err) => { assert.fail('unexpected'); - done(null); - }); + }).finally(done); }); test('issue #72798: null errors are hard to digest', function (done) { @@ -195,11 +187,9 @@ suite('RPCProtocol', () => { }; bProxy.$m(4, 1).then((res) => { assert.fail('unexpected'); - done(null); }, (err) => { assert.equal(err.what, 'what'); - done(null); - }); + }).finally(done); }); test('undefined arguments arrive as null', function () { diff --git a/src/vs/workbench/services/layout/browser/layoutService.ts b/src/vs/workbench/services/layout/browser/layoutService.ts index b1213ff2ce7..d8ff60045c5 100644 --- a/src/vs/workbench/services/layout/browser/layoutService.ts +++ b/src/vs/workbench/services/layout/browser/layoutService.ts @@ -33,9 +33,8 @@ export function positionToString(position: Position): string { case Position.LEFT: return 'left'; case Position.RIGHT: return 'right'; case Position.BOTTOM: return 'bottom'; + default: return 'bottom'; } - - return 'bottom'; } const positionsByString: { [key: string]: Position } = { diff --git a/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts b/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts index 69494b5d834..9ba5a4e9f1b 100644 --- a/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts @@ -203,7 +203,8 @@ suite('ExtHostTreeView', function () { assert.deepEqual(actuals, ['1/a', '1/b']); return testObject.$getChildren('testNodeWithIdTreeProvider', '1/a') .then(() => testObject.$getChildren('testNodeWithIdTreeProvider', '1/b')) - .then(() => { assert.fail('Should fail with duplicate id'); done(); }, () => done()); + .then(() => assert.fail('Should fail with duplicate id')) + .finally(done); }); }); onDidChangeTreeNode.fire(undefined); From eea74fd3a49145d9ca9c10e99ced68aeb55093a8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 17 Aug 2020 12:30:40 -0700 Subject: [PATCH 288/736] Only override copy/paste for extension page if webview is focused Fixes #102979 --- .../contrib/extensions/browser/extensions.contribution.ts | 2 +- .../workbench/contrib/webview/browser/baseWebviewElement.ts | 2 +- .../contrib/webview/browser/dynamicWebviewEditorOverlay.ts | 4 ++++ src/vs/workbench/contrib/webview/browser/webview.ts | 2 ++ .../contrib/webview/electron-browser/iframeWebviewElement.ts | 2 +- .../contrib/webview/electron-browser/webviewElement.ts | 4 ++-- 6 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index b6a43859392..547b5274ccc 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -472,7 +472,7 @@ function overrideActionForActiveExtensionEditorWebview(command: MultiCommand | u const editorService = accessor.get(IEditorService); const editor = editorService.activeEditorPane; if (editor instanceof ExtensionEditor) { - if (editor.activeWebview) { + if (editor.activeWebview?.isFocused) { f(editor.activeWebview); return true; } diff --git a/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts b/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts index 0291f0c605d..aaa5fb6d6b1 100644 --- a/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts @@ -74,7 +74,7 @@ export abstract class BaseWebview extends Disposable { protected get element(): T | undefined { return this._element; } private _focused: boolean | undefined; - protected get focused(): boolean { return !!this._focused; } + public get isFocused(): boolean { return !!this._focused; } private _state: WebviewState.State = new WebviewState.Initializing([]); diff --git a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts index ca03dc90b08..453b1040c2e 100644 --- a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts +++ b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts @@ -55,6 +55,10 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv this._findWidgetVisible = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE.bindTo(_contextKeyService); } + public get isFocused() { + return !!this._webview.value?.isFocused; + } + private readonly _onDispose = this._register(new Emitter()); public onDispose = this._onDispose.event; diff --git a/src/vs/workbench/contrib/webview/browser/webview.ts b/src/vs/workbench/contrib/webview/browser/webview.ts index 969dd6728bc..381e70b6ec7 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.ts @@ -93,6 +93,8 @@ export interface Webview extends IDisposable { initialScrollProgress: number; state: string | undefined; + readonly isFocused: boolean; + readonly onDidFocus: Event; readonly onDidBlur: Event; readonly onDidClickLink: Event; diff --git a/src/vs/workbench/contrib/webview/electron-browser/iframeWebviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/iframeWebviewElement.ts index 6e7570d370a..03d68b88996 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/iframeWebviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/iframeWebviewElement.ts @@ -122,7 +122,7 @@ export class ElectronIframeWebview extends IFrameWebview { // Workaround this by debouncing the focus and making sure we are not focused on an input // when we try to re-focus. this._focusDelayer.trigger(async () => { - if (!this.focused || !this.element) { + if (!this.isFocused || !this.element) { return; } diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 7421ee686ff..2ef9b4d1bcf 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -166,7 +166,7 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme this._myLogService.debug(`Webview(${this.id}): dom-ready`); // Workaround for https://github.com/electron/electron/issues/14474 - if (this.element && (this.focused || document.activeElement === this.element)) { + if (this.element && (this.isFocused || document.activeElement === this.element)) { this.element.blur(); this.element.focus(); } @@ -312,7 +312,7 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme // Workaround this by debouncing the focus and making sure we are not focused on an input // when we try to re-focus. this._focusDelayer.trigger(async () => { - if (!this.focused || !this.element) { + if (!this.isFocused || !this.element) { return; } From eb7a565926ce0c62b18242e8cbb3c3a692db9cb3 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Mon, 17 Aug 2020 15:13:04 -0700 Subject: [PATCH 289/736] Bump actions --- .github/workflows/author-verified.yml | 2 +- .github/workflows/commands.yml | 2 +- .github/workflows/deep-classifier-monitor.yml | 2 +- .github/workflows/deep-classifier-runner.yml | 2 +- .github/workflows/deep-classifier-scraper.yml | 2 +- .github/workflows/english-please.yml | 2 +- .github/workflows/feature-request.yml | 2 +- .github/workflows/latest-release-monitor.yml | 2 +- .github/workflows/locker.yml | 2 +- .github/workflows/needs-more-info-closer.yml | 2 +- .github/workflows/on-label.yml | 2 +- .github/workflows/on-open.yml | 2 +- .github/workflows/release-pipeline-labeler.yml | 2 +- .github/workflows/test-plan-item-validator.yml | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/author-verified.yml b/.github/workflows/author-verified.yml index c0f5efc51d4..167f27a5490 100644 --- a/.github/workflows/author-verified.yml +++ b/.github/workflows/author-verified.yml @@ -17,7 +17,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v33 + ref: v34 path: ./actions - name: Install Actions if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'author-verification-requested') diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml index 63e43a17703..61c82aa73ca 100644 --- a/.github/workflows/commands.yml +++ b/.github/workflows/commands.yml @@ -13,7 +13,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v33 + ref: v34 - name: Install Actions run: npm install --production --prefix ./actions - name: Run Commands diff --git a/.github/workflows/deep-classifier-monitor.yml b/.github/workflows/deep-classifier-monitor.yml index 545f74c273b..9425ae0f1f2 100644 --- a/.github/workflows/deep-classifier-monitor.yml +++ b/.github/workflows/deep-classifier-monitor.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v33 + ref: v34 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/deep-classifier-runner.yml b/.github/workflows/deep-classifier-runner.yml index 3db13a01c2b..2f598745e49 100644 --- a/.github/workflows/deep-classifier-runner.yml +++ b/.github/workflows/deep-classifier-runner.yml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v33 + ref: v34 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/deep-classifier-scraper.yml b/.github/workflows/deep-classifier-scraper.yml index a78b4d14c2f..b9cb6ec3cd3 100644 --- a/.github/workflows/deep-classifier-scraper.yml +++ b/.github/workflows/deep-classifier-scraper.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v33 + ref: v34 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/english-please.yml b/.github/workflows/english-please.yml index b81f12ab39f..fdfd548291d 100644 --- a/.github/workflows/english-please.yml +++ b/.github/workflows/english-please.yml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v33 + ref: v34 path: ./actions - name: Install Actions if: contains(github.event.issue.labels.*.name, '*english-please') diff --git a/.github/workflows/feature-request.yml b/.github/workflows/feature-request.yml index 9d25f4a9f5c..12af1423506 100644 --- a/.github/workflows/feature-request.yml +++ b/.github/workflows/feature-request.yml @@ -18,7 +18,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v33 + ref: v34 - name: Install Actions if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'feature-request') run: npm install --production --prefix ./actions diff --git a/.github/workflows/latest-release-monitor.yml b/.github/workflows/latest-release-monitor.yml index acca00a07e3..a5777e3a7f1 100644 --- a/.github/workflows/latest-release-monitor.yml +++ b/.github/workflows/latest-release-monitor.yml @@ -14,7 +14,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v33 + ref: v34 - name: Install Actions run: npm install --production --prefix ./actions - name: Install Storage Module diff --git a/.github/workflows/locker.yml b/.github/workflows/locker.yml index 776203c8c6c..64ac30d3717 100644 --- a/.github/workflows/locker.yml +++ b/.github/workflows/locker.yml @@ -14,7 +14,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v33 + ref: v34 - name: Install Actions run: npm install --production --prefix ./actions - name: Run Locker diff --git a/.github/workflows/needs-more-info-closer.yml b/.github/workflows/needs-more-info-closer.yml index 8ecdd5fb9be..018caf24fe0 100644 --- a/.github/workflows/needs-more-info-closer.yml +++ b/.github/workflows/needs-more-info-closer.yml @@ -14,7 +14,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v33 + ref: v34 - name: Install Actions run: npm install --production --prefix ./actions - name: Run Needs More Info Closer diff --git a/.github/workflows/on-label.yml b/.github/workflows/on-label.yml index 732842501a6..446c041ebd3 100644 --- a/.github/workflows/on-label.yml +++ b/.github/workflows/on-label.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v33 + ref: v34 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/on-open.yml b/.github/workflows/on-open.yml index 3c003d0797a..e4f0434dbdc 100644 --- a/.github/workflows/on-open.yml +++ b/.github/workflows/on-open.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v33 + ref: v34 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/release-pipeline-labeler.yml b/.github/workflows/release-pipeline-labeler.yml index 8386e8f0f9f..14f74f581dd 100644 --- a/.github/workflows/release-pipeline-labeler.yml +++ b/.github/workflows/release-pipeline-labeler.yml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v33 + ref: v34 path: ./actions - name: Checkout Repo if: github.event_name != 'issues' diff --git a/.github/workflows/test-plan-item-validator.yml b/.github/workflows/test-plan-item-validator.yml index 73da8e6b928..4365afa03e5 100644 --- a/.github/workflows/test-plan-item-validator.yml +++ b/.github/workflows/test-plan-item-validator.yml @@ -14,7 +14,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v33 + ref: v34 - name: Install Actions if: contains(github.event.issue.labels.*.name, 'testplan-item') || contains(github.event.issue.labels.*.name, 'invalid-testplan-item') run: npm install --production --prefix ./actions From 624e408a2c251ee20a49adb74a406ccc8bbd31f8 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 17 Aug 2020 17:09:40 -0700 Subject: [PATCH 290/736] allow customize contributions for notebook. --- .../contrib/notebook/browser/notebookBrowser.ts | 13 +++++++++++++ .../notebook/browser/notebookEditorExtensions.ts | 11 ++--------- .../notebook/browser/notebookEditorWidget.ts | 10 ++++++++-- .../notebook/browser/notebookEditorWidgetService.ts | 2 +- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 5b4949cbcbd..8374805c4c4 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -30,6 +30,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { CellLanguageStatusBarItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents'; import { EditorOptions } from 'vs/workbench/common/editor'; import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; +import { IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation'; export const KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED = new RawContextKey('notebookFindWidgetFocused', false); @@ -187,6 +188,18 @@ export class NotebookEditorOptions extends EditorOptions { } } +export type INotebookEditorContributionCtor = IConstructorSignature1; + +export interface INotebookEditorContributionDescription { + id: string; + ctor: INotebookEditorContributionCtor; +} + +export interface INotebookEditorWidgetOptions { + + contributions?: INotebookEditorContributionDescription[]; +} + export interface INotebookEditor extends IEditor { cursorNavigationMode: boolean; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorExtensions.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorExtensions.ts index 752a81728e1..01ea12b11a4 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorExtensions.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorExtensions.ts @@ -3,16 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BrandedService, IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation'; -import { INotebookEditor, INotebookEditorContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { BrandedService } from 'vs/platform/instantiation/common/instantiation'; +import { INotebookEditor, INotebookEditorContribution, INotebookEditorContributionCtor, INotebookEditorContributionDescription } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -export type INotebookEditorContributionCtor = IConstructorSignature1; - - -export interface INotebookEditorContributionDescription { - id: string; - ctor: INotebookEditorContributionCtor; -} class EditorContributionRegistry { public static readonly INSTANCE = new EditorContributionRegistry(); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index e0843061a75..378c52ad7c9 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -27,7 +27,7 @@ import { registerThemingParticipant } from 'vs/platform/theme/common/themeServic import { EditorMemento } from 'vs/workbench/browser/parts/editor/baseEditor'; import { IEditorMemento } from 'vs/workbench/common/editor'; import { CELL_MARGIN, CELL_RUN_GUTTER, CELL_TOP_MARGIN, SCROLLABLE_ELEMENT_PADDING_TOP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellEditState, CellFocusMode, ICellRange, ICellViewModel, INotebookCellList, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_OUTPUT_FOCUSED, INotebookDeltaDecoration, NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellEditState, CellFocusMode, ICellRange, ICellViewModel, INotebookCellList, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_OUTPUT_FOCUSED, INotebookDeltaDecoration, NotebookEditorOptions, INotebookEditorWidgetOptions, INotebookEditorContributionDescription } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList'; import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer'; @@ -203,6 +203,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } constructor( + private readonly editorWidgetOptions: INotebookEditorWidgetOptions, @IInstantiationService private readonly instantiationService: IInstantiationService, @IStorageService storageService: IStorageService, @INotebookService private notebookService: INotebookService, @@ -339,7 +340,12 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._notebookHasMultipleKernels = NOTEBOOK_HAS_MULTIPLE_KERNELS.bindTo(this.contextKeyService); this._notebookHasMultipleKernels.set(false); - const contributions = NotebookEditorExtensionsRegistry.getEditorContributions(); + let contributions: INotebookEditorContributionDescription[]; + if (Array.isArray(this.editorWidgetOptions.contributions)) { + contributions = this.editorWidgetOptions.contributions; + } else { + contributions = NotebookEditorExtensionsRegistry.getEditorContributions(); + } for (const desc of contributions) { try { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidgetService.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidgetService.ts index def8c4e957a..53548d58bb9 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidgetService.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidgetService.ts @@ -126,7 +126,7 @@ class NotebookEditorWidgetService implements INotebookEditorWidgetService { if (!value) { // NEW widget const instantiationService = accessor.get(IInstantiationService); - const widget = instantiationService.createInstance(NotebookEditorWidget); + const widget = instantiationService.createInstance(NotebookEditorWidget, {}); widget.createEditor(); const token = this._tokenPool++; value = { widget, token }; From 79ec737c179b38c2e373e5be64bb83cc29fa04e0 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 17 Aug 2020 17:33:52 -0700 Subject: [PATCH 291/736] update decorations --- .../contrib/notebook/browser/notebookDiffEditor.ts | 4 ++-- .../contrib/notebook/browser/notebookEditorWidget.ts | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts index f835bb8eaa8..3cae2991d7f 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts @@ -87,13 +87,13 @@ export class NotebookDiffEditor extends BaseEditor { await this._originalWidget.setModel(model.original.notebook, undefined); this._register(this._widget.onWillScroll(e => { - if (this._originalWidget) { + if (this._originalWidget && this._originalWidget.scrollTop !== e.scrollTop) { this._originalWidget.scrollTop = e.scrollTop; } })); this._register(this._originalWidget.onWillScroll(e => { - if (this._widget) { + if (this._widget && this._widget.scrollTop !== e.scrollTop) { this._widget.scrollTop = e.scrollTop; } })); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 96a3503b85c..22ad7de6fd8 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1962,15 +1962,17 @@ registerThemingParticipant((theme, collector) => { const addedBackground = theme.getColor(diffInserted); if (addedBackground) { - collector.addRule(`.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.nb-cell-added .cell-focus-indicator, - .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.nb-cell-added.markdown-cell-row { + collector.addRule(`.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row .nb-cell-added .cell-focus-indicator, + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row .nb-cell-added, + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-rowd.code-cell-row .nb-cell-adde { background-color: ${addedBackground} !important; }`); } const deletedBackground = theme.getColor(diffRemoved); if (deletedBackground) { - collector.addRule(`.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.nb-cell-deleted .cell-focus-indicator, - .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.nb-cell-deleted.markdown-cell-row { + collector.addRule(`.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row .nb-cell-deleted .cell-focus-indicator, + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row .nb-cell-deleted, + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row .nb-cell-deleted { background-color: ${deletedBackground} !important; }`); } From d320ea51b8fc10502a0eb03e3ec9d6926bc0b30b Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 17 Aug 2020 17:37:25 -0700 Subject: [PATCH 292/736] disable contributions in diff editor. --- .../workbench/contrib/notebook/browser/notebookDiffEditor.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts index 3cae2991d7f..590ee9b0db9 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts @@ -59,10 +59,10 @@ export class NotebookDiffEditor extends BaseEditor { // const group = this.group!; await super.setInput(input, options, token); - this._widget = this.instantiationService.createInstance(NotebookEditorWidget, { isEmbeded: true }); + this._widget = this.instantiationService.createInstance(NotebookEditorWidget, { isEmbeded: true, contributions: [] }); this._widget.createEditor(); - this._originalWidget = this.instantiationService.createInstance(NotebookEditorWidget, { isEmbeded: true }); + this._originalWidget = this.instantiationService.createInstance(NotebookEditorWidget, { isEmbeded: true, contributions: [] }); this._originalWidget.createEditor(); if (this._dimension) { From 9fcefcd87267441943f5a64ee1ba7e1a0f23565a Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 17 Aug 2020 17:38:45 -0700 Subject: [PATCH 293/736] Make sure "command palette" command doesn't show up in simple editor #104727 --- .../contrib/quickaccess/browser/quickAccess.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/quickaccess/browser/quickAccess.contribution.ts b/src/vs/workbench/contrib/quickaccess/browser/quickAccess.contribution.ts index 9e2ae84e031..895c3df7b10 100644 --- a/src/vs/workbench/contrib/quickaccess/browser/quickAccess.contribution.ts +++ b/src/vs/workbench/contrib/quickaccess/browser/quickAccess.contribution.ts @@ -95,10 +95,10 @@ MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { MenuRegistry.appendMenuItem(MenuId.EditorContext, { group: 'z_commands', + when: EditorContextKeys.editorSimpleInput.toNegated(), command: { id: ShowAllCommandsAction.ID, title: localize('commandPalette', "Command Palette..."), - precondition: EditorContextKeys.editorSimpleInput.toNegated() }, order: 1 }); From 650f7688f38ee61c7436e96fb36f3011d2e036ac Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 17 Aug 2020 17:48:17 -0700 Subject: [PATCH 294/736] Add `(loading...)` prefix to hovers while TS Server is starting up Fixes #104859 --- .../src/languageFeatures/hover.ts | 26 ++++++++++++++----- .../src/protocol.d.ts | 10 +++++++ .../src/test/server.test.ts | 3 ++- .../src/tsServer/server.ts | 12 +++++++-- .../src/tsServer/spawner.ts | 16 +++++++++++- .../src/typescriptService.ts | 5 ++++ 6 files changed, 62 insertions(+), 10 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/hover.ts b/extensions/typescript-language-features/src/languageFeatures/hover.ts index c6c4860f663..a4de074897f 100644 --- a/extensions/typescript-language-features/src/languageFeatures/hover.ts +++ b/extensions/typescript-language-features/src/languageFeatures/hover.ts @@ -5,7 +5,8 @@ import * as vscode from 'vscode'; import type * as Proto from '../protocol'; -import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; +import { localize } from '../tsServer/versionProvider'; +import { ClientCapability, ITypeScriptServiceClient, ServerType } from '../typescriptService'; import { conditionalRegistration, requireSomeCapability } from '../utils/dependentRegistration'; import { DocumentSelector } from '../utils/documentSelector'; import { markdownDocumentation } from '../utils/previewer'; @@ -35,17 +36,30 @@ class TypeScriptHoverProvider implements vscode.HoverProvider { } return new vscode.Hover( - TypeScriptHoverProvider.getContents(response.body), + this.getContents(response.body, response._serverType), typeConverters.Range.fromTextSpan(response.body)); } - private static getContents( - data: Proto.QuickInfoResponseBody + private getContents( + data: Proto.QuickInfoResponseBody, + source: ServerType | undefined, ) { - const parts = []; + const parts: vscode.MarkedString[] = []; if (data.displayString) { - parts.push({ language: 'typescript', value: data.displayString }); + const displayParts: string[] = []; + + if (source === ServerType.Syntax && this.client.capabilities.has(ClientCapability.Semantic)) { + displayParts.push( + localize({ + key: 'loadingPrefix', + comment: ['Prefix displayed for hover entries while the server is still loading'] + }, "(loading...)")); + } + + displayParts.push(data.displayString); + + parts.push({ language: 'typescript', value: displayParts.join(' ') }); } parts.push(markdownDocumentation(data.documentation, data.tags)); return parts; diff --git a/extensions/typescript-language-features/src/protocol.d.ts b/extensions/typescript-language-features/src/protocol.d.ts index 6e926eb8d7e..e81fe81f2db 100644 --- a/extensions/typescript-language-features/src/protocol.d.ts +++ b/extensions/typescript-language-features/src/protocol.d.ts @@ -1,2 +1,12 @@ import * as Proto from 'typescript/lib/protocol'; export = Proto; + +declare enum ServerType { + Syntax = 'syntax', + Semantic = 'semantic', +} +declare module 'typescript/lib/protocol' { + interface Response { + readonly _serverType?: ServerType; + } +} diff --git a/extensions/typescript-language-features/src/test/server.test.ts b/extensions/typescript-language-features/src/test/server.test.ts index 7e27e366b5c..5caad737a48 100644 --- a/extensions/typescript-language-features/src/test/server.test.ts +++ b/extensions/typescript-language-features/src/test/server.test.ts @@ -9,6 +9,7 @@ import * as stream from 'stream'; import type * as Proto from '../protocol'; import { NodeRequestCanceller } from '../tsServer/cancellation.electron'; import { ProcessBasedTsServer, TsServerProcess } from '../tsServer/server'; +import { ServerType } from '../typescriptService'; import { nulToken } from '../utils/cancellation'; import { Logger } from '../utils/logger'; import { TelemetryReporter } from '../utils/telemetry'; @@ -64,7 +65,7 @@ suite('Server', () => { test('should send requests with increasing sequence numbers', async () => { const process = new FakeServerProcess(); - const server = new ProcessBasedTsServer('semantic', process, undefined, new NodeRequestCanceller('semantic', tracer), undefined!, NoopTelemetryReporter, tracer); + const server = new ProcessBasedTsServer('semantic', ServerType.Semantic, process, undefined, new NodeRequestCanceller('semantic', tracer), undefined!, NoopTelemetryReporter, tracer); const onWrite1 = process.onWrite(); server.executeImpl('geterr', {}, { isAsync: false, token: nulToken, expectsResult: true }); diff --git a/extensions/typescript-language-features/src/tsServer/server.ts b/extensions/typescript-language-features/src/tsServer/server.ts index fc7841322bd..6ad3d015679 100644 --- a/extensions/typescript-language-features/src/tsServer/server.ts +++ b/extensions/typescript-language-features/src/tsServer/server.ts @@ -9,7 +9,7 @@ import { EventName } from '../protocol.const'; import { CallbackMap } from '../tsServer/callbackMap'; import { RequestItem, RequestQueue, RequestQueueingType } from '../tsServer/requestQueue'; import { TypeScriptServerError } from '../tsServer/serverError'; -import { ServerResponse, TypeScriptRequests } from '../typescriptService'; +import { ServerResponse, ServerType, TypeScriptRequests } from '../typescriptService'; import { TypeScriptServiceConfiguration } from '../utils/configuration'; import { Disposable } from '../utils/dispose'; import { TelemetryReporter } from '../utils/telemetry'; @@ -77,6 +77,7 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe constructor( private readonly _serverId: string, + private readonly _serverSource: ServerType, private readonly _process: TsServerProcess, private readonly _tsServerLogFile: string | undefined, private readonly _requestCanceller: OngoingRequestCanceller, @@ -130,7 +131,14 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe try { switch (message.type) { case 'response': - this.dispatchResponse(message as Proto.Response); + if (this._serverSource) { + this.dispatchResponse({ + ...(message as Proto.Response), + _serverType: this._serverSource + }); + } else { + this.dispatchResponse(message as Proto.Response); + } break; case 'event': diff --git a/extensions/typescript-language-features/src/tsServer/spawner.ts b/extensions/typescript-language-features/src/tsServer/spawner.ts index 70f1b41574e..7f4aaeefe13 100644 --- a/extensions/typescript-language-features/src/tsServer/spawner.ts +++ b/extensions/typescript-language-features/src/tsServer/spawner.ts @@ -6,7 +6,7 @@ import * as path from 'path'; import * as vscode from 'vscode'; import { OngoingRequestCancellerFactory } from '../tsServer/cancellation'; -import { ClientCapabilities, ClientCapability } from '../typescriptService'; +import { ClientCapabilities, ClientCapability, ServerType } from '../typescriptService'; import API from '../utils/api'; import { SeparateSyntaxServerConfiguration, TsServerLogLevel, TypeScriptServiceConfiguration } from '../utils/configuration'; import { Logger } from '../utils/logger'; @@ -143,6 +143,7 @@ export class TypeScriptServerSpawner { return new ProcessBasedTsServer( kind, + this.kindToServerType(kind), process!, tsServerLogFile, canceller, @@ -151,6 +152,19 @@ export class TypeScriptServerSpawner { this._tracer); } + private kindToServerType(kind: TsServerProcessKind): ServerType { + switch (kind) { + case TsServerProcessKind.Syntax: + return ServerType.Syntax; + + case TsServerProcessKind.Main: + case TsServerProcessKind.Semantic: + case TsServerProcessKind.Diagnostics: + default: + return ServerType.Semantic; + } + } + private getTsServerArgs( kind: TsServerProcessKind, configuration: TypeScriptServiceConfiguration, diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index de32927d816..8a59d97105c 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -13,6 +13,11 @@ import { TypeScriptServiceConfiguration } from './utils/configuration'; import { PluginManager } from './utils/plugins'; import { TelemetryReporter } from './utils/telemetry'; +export enum ServerType { + Syntax = 'syntax', + Semantic = 'semantic', +} + export namespace ServerResponse { export class Cancelled { From eefe53f0721d36618d30ccffe09b7cb16690d2bf Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 17 Aug 2020 17:49:11 -0700 Subject: [PATCH 295/736] update css per new layout. --- .../notebook/browser/notebookEditorWidget.ts | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 22ad7de6fd8..b03dfcc5622 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1954,25 +1954,35 @@ registerThemingParticipant((theme, collector) => { const modifiedBackground = theme.getColor(editorGutterModifiedBackground); if (modifiedBackground) { - collector.addRule(`.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.nb-cell-modified .cell-focus-indicator, - .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.nb-cell-modified.markdown-cell-row { + collector.addRule(` + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row .nb-cell-modified .cell-focus-indicator { + background-color: ${modifiedBackground} !important; + } + + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row .nb-cell-modified { background-color: ${modifiedBackground} !important; }`); } const addedBackground = theme.getColor(diffInserted); if (addedBackground) { - collector.addRule(`.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row .nb-cell-added .cell-focus-indicator, - .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row .nb-cell-added, - .monaco-workbench .notebookOverlay .monaco-list .monaco-list-rowd.code-cell-row .nb-cell-adde { + collector.addRule(` + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row .nb-cell-added .cell-focus-indicator { + background-color: ${addedBackground} !important; + } + + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row .nb-cell-added { background-color: ${addedBackground} !important; }`); } const deletedBackground = theme.getColor(diffRemoved); if (deletedBackground) { - collector.addRule(`.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row .nb-cell-deleted .cell-focus-indicator, - .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row .nb-cell-deleted, - .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row .nb-cell-deleted { + collector.addRule(` + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row .nb-cell-deleted .cell-focus-indicator { + background-color: ${deletedBackground} !important; + } + + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row .nb-cell-deleted { background-color: ${deletedBackground} !important; }`); } From 63c711deef8e2c41b484f6b9cf0151267ac111f7 Mon Sep 17 00:00:00 2001 From: Duc Nghiem Xuan Date: Tue, 18 Aug 2020 09:52:26 +0900 Subject: [PATCH 296/736] Don't include this function types in completeFunctionCalls (#104479) --- .../src/test/functionCallSnippet.test.ts | 9 +++++++++ .../src/utils/snippetForFunctionCall.ts | 4 +++- 2 files changed, 12 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 1289b87b4bc..5c8cf18c73c 100644 --- a/extensions/typescript-language-features/src/test/functionCallSnippet.test.ts +++ b/extensions/typescript-language-features/src/test/functionCallSnippet.test.ts @@ -128,4 +128,13 @@ suite('typescript function call snippets', () => { ).snippet.value, 'foobar(${1:param})$0'); }); + + test('Should skip over this parameter', async () => { + assert.strictEqual( + snippetForFunctionCall( + { label: 'foobar', }, + [{ "text": "function", "kind": "keyword" }, { "text": " ", "kind": "space" }, { "text": "foobar", "kind": "functionName" }, { "text": "(", "kind": "punctuation" }, { "text": "this", "kind": "parameterName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "string", "kind": "keyword" }, { "text": ",", "kind": "punctuation" }, { "text": "param", "kind": "parameterName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "string", "kind": "keyword" }, { "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 6faf19eff48..e0185db581b 100644 --- a/extensions/typescript-language-features/src/utils/snippetForFunctionCall.ts +++ b/extensions/typescript-language-features/src/utils/snippetForFunctionCall.ts @@ -73,7 +73,9 @@ function getParameterListParts( const next = displayParts[i + 1]; // Skip optional parameters const nameIsFollowedByOptionalIndicator = next && next.text === '?'; - if (!nameIsFollowedByOptionalIndicator) { + // Skip this parameter + const nameIsThis = part.text === 'this'; + if (!nameIsFollowedByOptionalIndicator && !nameIsThis) { parts.push(part); } hasOptionalParameters = hasOptionalParameters || nameIsFollowedByOptionalIndicator; From 6b01ac19edd304bd119127ae9e3ae02af7124894 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Tue, 18 Aug 2020 00:02:31 -0400 Subject: [PATCH 297/736] Fixes #104700 - use registered provider count Don't account for excluded providers, since the user can change those dynamically and we don't want the view to hide itself --- .../workbench/contrib/timeline/common/timelineService.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/timeline/common/timelineService.ts b/src/vs/workbench/contrib/timeline/common/timelineService.ts index 2d2d5e76d5a..39a064a9a6f 100644 --- a/src/vs/workbench/contrib/timeline/common/timelineService.ts +++ b/src/vs/workbench/contrib/timeline/common/timelineService.ts @@ -271,12 +271,6 @@ export class TimelineService implements ITimelineService { } private updateHasProviderContext() { - if (this.providers.size === 0) { - this.hasProviderContext.set(false); - return; - } - - const hasProviders = [...this.providers.keys()].some(id => !this.excludedSources.has(id)); - this.hasProviderContext.set(hasProviders); + this.hasProviderContext.set(this.providers.size !== 0); } } From ace080a622ef5be43b074970cc3847be646fed1a Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Mon, 17 Aug 2020 23:12:03 -0500 Subject: [PATCH 298/736] Fixes #104776 --- src/vs/base/browser/markdownRenderer.ts | 40 +++++++++++++++---------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index e79f159100b..48ead97d48f 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -17,6 +17,7 @@ import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { renderCodicons, markdownEscapeEscapedCodicons } from 'vs/base/common/codicons'; import { resolvePath } from 'vs/base/common/resources'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; export interface MarkedOptions extends marked.MarkedOptions { baseUrl?: never; @@ -171,25 +172,32 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende const actionHandler = options.actionHandler; if (actionHandler) { - actionHandler.disposeables.add(DOM.addStandardDisposableListener(element, 'click', event => { - let target: HTMLElement | null = event.target; - if (target.tagName !== 'A') { - target = target.parentElement; - if (!target || target.tagName !== 'A') { + [DOM.EventType.CLICK, DOM.EventType.AUXCLICK].forEach(event => { + actionHandler.disposeables.add(DOM.addDisposableListener(element, event, (e: MouseEvent) => { + const mouseEvent = new StandardMouseEvent(e); + if (!mouseEvent.leftButton && !mouseEvent.middleButton) { return; } - } - try { - const href = target.dataset['href']; - if (href) { - actionHandler.callback(href, event); + + let target: HTMLElement | null = mouseEvent.target; + if (target.tagName !== 'A') { + target = target.parentElement; + if (!target || target.tagName !== 'A') { + return; + } } - } catch (err) { - onUnexpectedError(err); - } finally { - event.preventDefault(); - } - })); + try { + const href = target.dataset['href']; + if (href) { + actionHandler.callback(href, mouseEvent); + } + } catch (err) { + onUnexpectedError(err); + } finally { + mouseEvent.preventDefault(); + } + })); + }); } // Use our own sanitizer so that we can let through only spans. From ba92c47e81edaeb5ef6446699dcfaba2593e53bb Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 17 Aug 2020 22:04:24 -0700 Subject: [PATCH 299/736] range map provider --- src/vs/base/browser/ui/list/listView.ts | 11 +- src/vs/base/browser/ui/list/listWidget.ts | 3 +- src/vs/base/browser/ui/list/rangeMap.ts | 148 +---------- .../notebook/browser/notebookDiffEditor.ts | 42 +++ .../notebook/browser/notebookEditorWidget.ts | 4 + .../browser/view/rangeMapWithWhitespace.ts | 247 ++++++++++++++++++ .../common/model/notebookCellTextModel.ts | 4 +- 7 files changed, 310 insertions(+), 149 deletions(-) create mode 100644 src/vs/workbench/contrib/notebook/browser/view/rangeMapWithWhitespace.ts diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 7421ac40d40..5a14b2d4bec 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -54,6 +54,10 @@ export interface IListViewOptionsUpdate { readonly horizontalScrolling?: boolean; } +export interface IListViewRangeMapProvider { + (): RangeMap; +} + export interface IListViewOptions extends IListViewOptionsUpdate { readonly dnd?: IListViewDragAndDrop; readonly useShadows?: boolean; @@ -64,6 +68,7 @@ export interface IListViewOptions extends IListViewOptionsUpdate { readonly mouseSupport?: boolean; readonly accessibilityProvider?: IListViewAccessibilityProvider; readonly transformOptimization?: boolean; + readonly rangeMapProvider?: IListViewRangeMapProvider; } const DefaultOptions = { @@ -209,6 +214,7 @@ export class ListView implements ISpliceable, IDisposable { private items: IItem[]; private itemId: number; private rangeMap: RangeMap; + private rangeMapProvider: IListViewRangeMapProvider; private cache: RowCache; private renderers = new Map>(); private lastRenderTop: number; @@ -289,7 +295,8 @@ export class ListView implements ISpliceable, IDisposable { this.items = []; this.itemId = 0; - this.rangeMap = new RangeMap(); + this.rangeMapProvider = getOrDefault(options, o => o.rangeMapProvider, () => new RangeMap()); + this.rangeMap = this.rangeMapProvider(); for (const renderer of renderers) { this.renderers.set(renderer.templateId, renderer); @@ -469,7 +476,7 @@ export class ListView implements ISpliceable, IDisposable { // TODO@joao: improve this optimization to catch even more cases if (start === 0 && deleteCount >= this.items.length) { - this.rangeMap = new RangeMap(); + this.rangeMap = this.rangeMapProvider(); this.rangeMap.splice(0, 0, inserted); this.items = inserted; deleted = []; diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 252150b28d7..e5fa1ebe7b9 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -16,7 +16,7 @@ import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardE 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, ListError, IKeyboardNavigationDelegate } from './list'; -import { ListView, IListViewOptions, IListViewDragAndDrop, IListViewAccessibilityProvider, IListViewOptionsUpdate } from './listView'; +import { ListView, IListViewOptions, IListViewDragAndDrop, IListViewAccessibilityProvider, IListViewOptionsUpdate, IListViewRangeMapProvider } from './listView'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; import { ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable'; @@ -854,6 +854,7 @@ export interface IListOptions { readonly horizontalScrolling?: boolean; readonly additionalScrollHeight?: number; readonly transformOptimization?: boolean; + readonly rangeMapProvider?: IListViewRangeMapProvider; readonly smoothScrolling?: boolean; } diff --git a/src/vs/base/browser/ui/list/rangeMap.ts b/src/vs/base/browser/ui/list/rangeMap.ts index d6764a778b7..e9360d1173f 100644 --- a/src/vs/base/browser/ui/list/rangeMap.ts +++ b/src/vs/base/browser/ui/list/rangeMap.ts @@ -83,25 +83,13 @@ export function consolidate(groups: IRangedGroup[]): IRangedGroup[] { * Concatenates several collections of ranged groups into a single * collection. */ -function concat(...groups: IRangedGroup[][]): IRangedGroup[] { +export function concat(...groups: IRangedGroup[][]): IRangedGroup[] { return consolidate(groups.reduce((r, g) => r.concat(g), [])); } -export class ListWhitespace { - - constructor( - public afterIndex: number, - public height: number, - // height of all whitespaces before this whitespace (inclusive) - public prefixSum: number - ) { } -} - -// [ { start: 0, len: 2, size: 2 }, { start: 2, len: 1, size: 3 }, {} ] export class RangeMap { private groups: IRangedGroup[] = []; - private whitespaces: ListWhitespace[] = []; private _size = 0; splice(index: number, deleteCount: number, items: IItem[] = []): void { @@ -117,86 +105,6 @@ export class RangeMap { this.groups = concat(before, middle, after); this._size = this.groups.reduce((t, g) => t + (g.size * (g.range.end - g.range.start)), 0); - - const deleteRange = deleteCount > 0 ? [index, index + deleteCount - 1] : []; - const indexDelta = items.length - deleteCount; - let prefixSumDelta = 0; - const pendingRemovalWhitespace: number[] = []; - for (let i = 0; i < this.whitespaces.length; i++) { - const whitespace = this.whitespaces[i]; - - if (whitespace.afterIndex < index) { - continue; - } else if (deleteRange.length > 0 && whitespace.afterIndex >= deleteRange[0] && whitespace.afterIndex <= deleteRange[1]) { - // should be deleted - pendingRemovalWhitespace.push(i); - prefixSumDelta += whitespace.height; - } else { - whitespace.afterIndex += indexDelta; - whitespace.prefixSum -= prefixSumDelta; - } - } - - pendingRemovalWhitespace.reverse().forEach(index => { - this.whitespaces.splice(index, 1); - }); - } - - public static findInsertionIndex(arr: ListWhitespace[], afterIndex: number): number { - let low = 0; - let high = arr.length; - - while (low < high) { - const mid = ((low + high) >>> 1); - - if (afterIndex === arr[mid].afterIndex) { - low = mid; - break; - } else if (afterIndex < arr[mid].afterIndex) { - high = mid; - } else { - low = mid + 1; - } - } - - return low; - } - - insertWhitespace(afterIndex: number, height: number) { - // TODO - // 2. delay prefix sum update - const insertIndex = RangeMap.findInsertionIndex(this.whitespaces, afterIndex); - const prefixSum = insertIndex > 0 ? this.whitespaces[insertIndex - 1].prefixSum + height : height; - const insertedItem = new ListWhitespace(afterIndex, height, prefixSum); - this.whitespaces.splice(insertIndex, 0, insertedItem); - - for (let i = insertIndex + 1; i < this.whitespaces.length; i++) { - this.whitespaces[i].prefixSum += height; - } - } - - // todo, allow multiple whitespaces after one index - updateWhitespace(afterIndex: number, newHeight: number) { - let delta = 0; - let findWhitespace = false; - for (let i = 0; i < this.whitespaces.length; i++) { - if (this.whitespaces[i].afterIndex === afterIndex) { - delta = newHeight - this.whitespaces[i].height; - this.whitespaces[i].height = newHeight; - this.whitespaces[i].prefixSum += delta; - findWhitespace = true; - } else if (this.whitespaces[i].afterIndex > afterIndex) { - if (!findWhitespace) { - this.insertWhitespace(afterIndex, newHeight); - return; - } - this.whitespaces[i].prefixSum += delta; - } - } - - if (!findWhitespace) { - this.insertWhitespace(afterIndex, newHeight); - } } /** @@ -216,42 +124,7 @@ export class RangeMap { * Returns the sum of the sizes of all items in the range map. */ get size(): number { - return this._size - + (this.whitespaces.length ? this.whitespaces[this.whitespaces.length - 1]?.prefixSum : 0); - } - - private _getWhitespaceAccumulatedHeightBeforeIndex(index: number): number { - const lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeIndex(index); - - if (lastWhitespaceBeforeLineNumber === -1) { - return 0; - } - - return this.whitespaces[lastWhitespaceBeforeLineNumber].prefixSum; - } - - private _findLastWhitespaceBeforeIndex(index: number): number { - const arr = this.whitespaces; - let low = 0; - let high = arr.length - 1; - - while (low <= high) { - const delta = (high - low) | 0; - const halfDelta = (delta / 2) | 0; - const mid = (low + halfDelta) | 0; - - if (arr[mid].afterIndex < index) { - if (mid + 1 >= arr.length || arr[mid + 1].afterIndex >= index) { - return mid; - } else { - low = (mid + 1) | 0; - } - } else { - high = (mid - 1) | 0; - } - } - - return -1; + return this._size; } /** @@ -269,20 +142,7 @@ export class RangeMap { const count = group.range.end - group.range.start; const newSize = size + (count * group.size); - if (position < newSize + this._getWhitespaceAccumulatedHeightBeforeIndex(group.range.end + 1)) { - // try to find the right index - let currSize = size; - // position > currSize + all whitespaces before current range - for (let j = group.range.start; j < group.range.end; j++) { - currSize = currSize + group.size; - - if (position >= currSize + this._getWhitespaceAccumulatedHeightBeforeIndex(j + 1)) { - continue; - } else { - return j; - } - } - + if (position < newSize) { return index + Math.floor((position - size) / group.size); } @@ -317,7 +177,7 @@ export class RangeMap { const newCount = count + groupCount; if (index < newCount) { - return position + ((index - count) * group.size) + this._getWhitespaceAccumulatedHeightBeforeIndex(index); + return position + ((index - count) * group.size); } position += groupCount * group.size; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts index 590ee9b0db9..e3a815104a5 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts @@ -24,6 +24,7 @@ import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewMod import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; import { FileService } from 'vs/platform/files/common/fileService'; import { IFileService } from 'vs/platform/files/common/files'; +import { DiffComputer } from 'vs/editor/common/diff/diffComputer'; export class NotebookDiffEditor extends BaseEditor { @@ -171,6 +172,47 @@ export class NotebookDiffEditor extends BaseEditor { ]); }); + // console.log(diffResult); + + // diffResult.changes.forEach(change => { + // if (change.modifiedLength === 0) { + // // deletion ... + // return; + // } + + // if (change.originalLength === 0) { + // // insertion + // return; + // } + + // for (let i = 0, len = Math.min(change.modifiedLength, change.originalLength); i < len; i++) { + // let originalIndex = change.originalStart + i; + // let modifiedIndex = change.modifiedStart + i; + + // const originalCell = this._originalWidget!.viewModel!.viewCells[originalIndex]; + // const modifiedCell = this._widget!.viewModel!.viewCells[modifiedIndex]; + + // if (originalCell.getText() !== modifiedCell.getText()) { + // console.log(`original cell ${originalIndex} content change`); + // const originalLines = originalCell.textBuffer.getLinesContent(); + // const modifiedLines = modifiedCell.textBuffer.getLinesContent(); + // const diffComputer = new DiffComputer(originalLines, modifiedLines, { + // shouldComputeCharChanges: true, + // shouldPostProcessCharChanges: true, + // shouldIgnoreTrimWhitespace: false, + // shouldMakePrettyDiff: true, + // maxComputationTime: 5000 + // }); + + // const diffResult = diffComputer.computeDiff(); + // console.log(diffResult); + // } else { + // console.log(`original cell ${originalIndex} metadata change`) + // } + + // } + // }); + this._originalCellDecorations = this._originalWidget.deltaCellDecorations(this._originalCellDecorations, originalDecorations); this._cellDecorations = this._widget.deltaCellDecorations(this._cellDecorations, modifiedDecorations); } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index b03dfcc5622..5fbd4dc1e41 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -60,6 +60,7 @@ import { isMacintosh, isNative } from 'vs/base/common/platform'; import { getTitleBarStyle } from 'vs/platform/windows/common/windows'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { CellDragAndDropController } from 'vs/workbench/contrib/notebook/browser/view/renderers/dnd'; +import { RangeMapWithWhitespace } from 'vs/workbench/contrib/notebook/browser/view/rangeMapWithWhitespace'; const $ = DOM.$; @@ -413,6 +414,9 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor enableKeyboardNavigation: true, additionalScrollHeight: 0, transformOptimization: (isMacintosh && isNative) || getTitleBarStyle(this.configurationService, this.environmentService) === 'native', + rangeMapProvider: () => { + return new RangeMapWithWhitespace(); + }, styleController: (_suffix: string) => { return this._list!; }, overrideStyles: { listBackground: editorBackground, diff --git a/src/vs/workbench/contrib/notebook/browser/view/rangeMapWithWhitespace.ts b/src/vs/workbench/contrib/notebook/browser/view/rangeMapWithWhitespace.ts new file mode 100644 index 00000000000..bf2adb1b1a0 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/view/rangeMapWithWhitespace.ts @@ -0,0 +1,247 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { concat, groupIntersect, IItem, IRangedGroup, shift } from 'vs/base/browser/ui/list/rangeMap'; + +export class ListWhitespace { + + constructor( + public afterIndex: number, + public height: number, + // height of all whitespaces before this whitespace (inclusive) + public prefixSum: number + ) { } +} + +// [ { start: 0, len: 2, size: 2 }, { start: 2, len: 1, size: 3 }, {} ] +export class RangeMapWithWhitespace { + + private groups: IRangedGroup[] = []; + private whitespaces: ListWhitespace[] = []; + private _size = 0; + + splice(index: number, deleteCount: number, items: IItem[] = []): void { + const diff = items.length - deleteCount; + const before = groupIntersect({ start: 0, end: index }, this.groups); + const after = groupIntersect({ start: index + deleteCount, end: Number.POSITIVE_INFINITY }, this.groups) + .map(g => ({ range: shift(g.range, diff), size: g.size })); + + const middle = items.map((item, i) => ({ + range: { start: index + i, end: index + i + 1 }, + size: item.size + })); + + this.groups = concat(before, middle, after); + this._size = this.groups.reduce((t, g) => t + (g.size * (g.range.end - g.range.start)), 0); + + const deleteRange = deleteCount > 0 ? [index, index + deleteCount - 1] : []; + const indexDelta = items.length - deleteCount; + let prefixSumDelta = 0; + const pendingRemovalWhitespace: number[] = []; + for (let i = 0; i < this.whitespaces.length; i++) { + const whitespace = this.whitespaces[i]; + + if (whitespace.afterIndex < index) { + continue; + } else if (deleteRange.length > 0 && whitespace.afterIndex >= deleteRange[0] && whitespace.afterIndex <= deleteRange[1]) { + // should be deleted + pendingRemovalWhitespace.push(i); + prefixSumDelta += whitespace.height; + } else { + whitespace.afterIndex += indexDelta; + whitespace.prefixSum -= prefixSumDelta; + } + } + + pendingRemovalWhitespace.reverse().forEach(index => { + this.whitespaces.splice(index, 1); + }); + } + + public static findInsertionIndex(arr: ListWhitespace[], afterIndex: number): number { + let low = 0; + let high = arr.length; + + while (low < high) { + const mid = ((low + high) >>> 1); + + if (afterIndex === arr[mid].afterIndex) { + low = mid; + break; + } else if (afterIndex < arr[mid].afterIndex) { + high = mid; + } else { + low = mid + 1; + } + } + + return low; + } + + insertWhitespace(afterIndex: number, height: number) { + // TODO + // 2. delay prefix sum update + const insertIndex = RangeMapWithWhitespace.findInsertionIndex(this.whitespaces, afterIndex); + const prefixSum = insertIndex > 0 ? this.whitespaces[insertIndex - 1].prefixSum + height : height; + const insertedItem = new ListWhitespace(afterIndex, height, prefixSum); + this.whitespaces.splice(insertIndex, 0, insertedItem); + + for (let i = insertIndex + 1; i < this.whitespaces.length; i++) { + this.whitespaces[i].prefixSum += height; + } + } + + // todo, allow multiple whitespaces after one index + updateWhitespace(afterIndex: number, newHeight: number) { + let delta = 0; + let findWhitespace = false; + for (let i = 0; i < this.whitespaces.length; i++) { + if (this.whitespaces[i].afterIndex === afterIndex) { + delta = newHeight - this.whitespaces[i].height; + this.whitespaces[i].height = newHeight; + this.whitespaces[i].prefixSum += delta; + findWhitespace = true; + } else if (this.whitespaces[i].afterIndex > afterIndex) { + if (!findWhitespace) { + this.insertWhitespace(afterIndex, newHeight); + return; + } + this.whitespaces[i].prefixSum += delta; + } + } + + if (!findWhitespace) { + this.insertWhitespace(afterIndex, newHeight); + } + } + + /** + * Returns the number of items in the range map. + */ + get count(): number { + const len = this.groups.length; + + if (!len) { + return 0; + } + + return this.groups[len - 1].range.end; + } + + /** + * Returns the sum of the sizes of all items in the range map. + */ + get size(): number { + return this._size + + (this.whitespaces.length ? this.whitespaces[this.whitespaces.length - 1]?.prefixSum : 0); + } + + private _getWhitespaceAccumulatedHeightBeforeIndex(index: number): number { + const lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeIndex(index); + + if (lastWhitespaceBeforeLineNumber === -1) { + return 0; + } + + return this.whitespaces[lastWhitespaceBeforeLineNumber].prefixSum; + } + + private _findLastWhitespaceBeforeIndex(index: number): number { + const arr = this.whitespaces; + let low = 0; + let high = arr.length - 1; + + while (low <= high) { + const delta = (high - low) | 0; + const halfDelta = (delta / 2) | 0; + const mid = (low + halfDelta) | 0; + + if (arr[mid].afterIndex < index) { + if (mid + 1 >= arr.length || arr[mid + 1].afterIndex >= index) { + return mid; + } else { + low = (mid + 1) | 0; + } + } else { + high = (mid - 1) | 0; + } + } + + return -1; + } + + /** + * Returns the index of the item at the given position. + */ + indexAt(position: number): number { + if (position < 0) { + return -1; + } + + let index = 0; + let size = 0; + + for (let group of this.groups) { + const count = group.range.end - group.range.start; + const newSize = size + (count * group.size); + + if (position < newSize + this._getWhitespaceAccumulatedHeightBeforeIndex(group.range.end + 1)) { + // try to find the right index + let currSize = size; + // position > currSize + all whitespaces before current range + for (let j = group.range.start; j < group.range.end; j++) { + currSize = currSize + group.size; + + if (position >= currSize + this._getWhitespaceAccumulatedHeightBeforeIndex(j + 1)) { + continue; + } else { + return j; + } + } + + return index + Math.floor((position - size) / group.size); + } + + index += count; + size = newSize; + } + + return index; + } + + /** + * Returns the index of the item right after the item at the + * index of the given position. + */ + indexAfter(position: number): number { + return Math.min(this.indexAt(position) + 1, this.count); + } + + /** + * Returns the start position of the item at the given index. + */ + positionAt(index: number): number { + if (index < 0) { + return -1; + } + + let position = 0; + let count = 0; + + for (let group of this.groups) { + const groupCount = group.range.end - group.range.start; + const newCount = count + groupCount; + + if (index < newCount) { + return position + ((index - count) * group.size) + this._getWhitespaceAccumulatedHeightBeforeIndex(index); + } + + position += groupCount * group.size; + count = newCount; + } + + return -1; + } +} diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts index f2c035c987b..43356373703 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts @@ -108,8 +108,8 @@ export class NotebookCellTextModel extends Disposable implements ICell { return this._hash; } - // this._hash = hash([hash(this.getValue()), this._metadata]); - this._hash = hash(this.getValue()); + this._hash = hash([hash(this.getValue()), this._metadata]); + // this._hash = hash(this.getValue()); return this._hash; } From e62ccab719a32275d31f6a4e3b6af4b76f6f1889 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 17 Aug 2020 22:14:01 -0700 Subject: [PATCH 300/736] minimal list view change --- src/vs/base/browser/ui/list/listView.ts | 10 -- src/vs/base/browser/ui/list/rangeMap.ts | 2 +- .../test/browser/ui/list/rangeMap.test.ts | 157 ---------------- .../notebook/browser/notebookDiffEditor.ts | 1 - .../notebook/browser/notebookEditorWidget.ts | 4 - .../notebook/browser/view/notebookCellList.ts | 15 +- .../browser/view/rangeMapWithWhitespace.ts | 36 ++-- .../notebook/test/notebookCommon.test.ts | 170 ++++++++++++++++++ 8 files changed, 205 insertions(+), 190 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 5a14b2d4bec..f97f0b98008 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -382,16 +382,6 @@ export class ListView implements ISpliceable, IDisposable { this.scrollableElement.triggerScrollFromMouseWheelEvent(browserEvent); } - insertWhitespace(index: number, height: number) { - this.rangeMap.insertWhitespace(index, height); - this.rerender(); - } - - updateWhitespace(index: number, newHeight: number) { - this.rangeMap.updateWhitespace(index, newHeight); - this.rerender(); - } - updateElementHeight(index: number, size: number, anchorIndex: number | null): void { if (index < 0 || index >= this.items.length) { return; diff --git a/src/vs/base/browser/ui/list/rangeMap.ts b/src/vs/base/browser/ui/list/rangeMap.ts index e9360d1173f..8a732eb4460 100644 --- a/src/vs/base/browser/ui/list/rangeMap.ts +++ b/src/vs/base/browser/ui/list/rangeMap.ts @@ -83,7 +83,7 @@ export function consolidate(groups: IRangedGroup[]): IRangedGroup[] { * Concatenates several collections of ranged groups into a single * collection. */ -export function concat(...groups: IRangedGroup[][]): IRangedGroup[] { +function concat(...groups: IRangedGroup[][]): IRangedGroup[] { return consolidate(groups.reduce((r, g) => r.concat(g), [])); } diff --git a/src/vs/base/test/browser/ui/list/rangeMap.test.ts b/src/vs/base/test/browser/ui/list/rangeMap.test.ts index 8805ea8a2cc..c92612c1454 100644 --- a/src/vs/base/test/browser/ui/list/rangeMap.test.ts +++ b/src/vs/base/test/browser/ui/list/rangeMap.test.ts @@ -341,162 +341,5 @@ suite('RangeMap', () => { assert.equal(rangeMap.positionAt(3), 30); assert.equal(rangeMap.positionAt(4), -1); }); - - test('insert whitespace 1', () => { - rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); - assert.equal(rangeMap.indexAt(0), 0); - assert.equal(rangeMap.indexAt(1), 1); - assert.equal(rangeMap.indexAt(5), 5); - assert.equal(rangeMap.indexAt(9), 9); - assert.equal(rangeMap.indexAt(10), 10); - assert.equal(rangeMap.indexAt(11), 10); - - for (let i = 0; i < 10; i++) { - rangeMap.insertWhitespace(i, 1); - } - - assert.equal(rangeMap.indexAt(0), 0); - assert.equal(rangeMap.indexAt(1), 0); - assert.equal(rangeMap.indexAt(2), 1); - assert.equal(rangeMap.indexAt(3), 1); - assert.equal(rangeMap.indexAt(4), 2); - assert.equal(rangeMap.indexAt(5), 2); - assert.equal(rangeMap.indexAt(6), 3); - assert.equal(rangeMap.indexAt(7), 3); - assert.equal(rangeMap.indexAt(8), 4); - assert.equal(rangeMap.indexAt(9), 4); - }); - - test('insert whitespace 2', () => { - rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); - assert.equal(rangeMap.indexAt(0), 0); - assert.equal(rangeMap.indexAt(1), 1); - assert.equal(rangeMap.indexAt(5), 5); - assert.equal(rangeMap.indexAt(9), 9); - assert.equal(rangeMap.indexAt(10), 10); - assert.equal(rangeMap.indexAt(11), 10); - - rangeMap.insertWhitespace(0, 2); - assert.equal(rangeMap.indexAt(0), 0); - assert.equal(rangeMap.indexAt(1), 0); - assert.equal(rangeMap.indexAt(2), 0); - assert.equal(rangeMap.indexAt(3), 1); - assert.equal(rangeMap.indexAt(4), 2); - assert.equal(rangeMap.indexAt(5), 3); - assert.equal(rangeMap.indexAt(10), 8); - - rangeMap.insertWhitespace(3, 3); - assert.equal(rangeMap.indexAt(0), 0); - assert.equal(rangeMap.indexAt(1), 0); - assert.equal(rangeMap.indexAt(2), 0); - assert.equal(rangeMap.indexAt(3), 1); - assert.equal(rangeMap.indexAt(4), 2); - assert.equal(rangeMap.indexAt(5), 3); - assert.equal(rangeMap.indexAt(6), 3); - assert.equal(rangeMap.indexAt(7), 3); - assert.equal(rangeMap.indexAt(8), 3); - assert.equal(rangeMap.indexAt(9), 4); - assert.equal(rangeMap.indexAt(10), 5); - }); - - test('insert whitespace 3', () => { - rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); - assert.equal(rangeMap.indexAt(0), 0); - assert.equal(rangeMap.indexAt(1), 1); - assert.equal(rangeMap.indexAt(5), 5); - assert.equal(rangeMap.indexAt(9), 9); - assert.equal(rangeMap.indexAt(10), 10); - assert.equal(rangeMap.indexAt(11), 10); - - rangeMap.insertWhitespace(0, 2); - assert.equal(rangeMap.indexAt(0), 0); - assert.equal(rangeMap.indexAt(1), 0); - assert.equal(rangeMap.indexAt(2), 0); - assert.equal(rangeMap.indexAt(3), 1); - assert.equal(rangeMap.indexAt(4), 2); - assert.equal(rangeMap.indexAt(5), 3); - assert.equal(rangeMap.indexAt(10), 8); - - rangeMap.insertWhitespace(3, 3); - rangeMap.updateWhitespace(0, 3); - assert.equal(rangeMap.indexAt(0), 0); - assert.equal(rangeMap.indexAt(1), 0); - assert.equal(rangeMap.indexAt(2), 0); - assert.equal(rangeMap.indexAt(3), 0); - assert.equal(rangeMap.indexAt(4), 1); - assert.equal(rangeMap.indexAt(5), 2); - assert.equal(rangeMap.indexAt(6), 3); - assert.equal(rangeMap.indexAt(7), 3); - assert.equal(rangeMap.indexAt(8), 3); - assert.equal(rangeMap.indexAt(9), 3); - assert.equal(rangeMap.indexAt(10), 4); - }); - - test('insert whitespace, positionAt', () => { - rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); - assert.equal(rangeMap.positionAt(0), 0); - assert.equal(rangeMap.positionAt(1), 1); - assert.equal(rangeMap.positionAt(5), 5); - assert.equal(rangeMap.positionAt(9), 9); - - rangeMap.insertWhitespace(0, 2); - assert.equal(rangeMap.positionAt(0), 0); - assert.equal(rangeMap.positionAt(1), 3); - assert.equal(rangeMap.positionAt(2), 4); - assert.equal(rangeMap.positionAt(3), 5); - }); - - test('insert whitespace, positionAt 2', () => { - rangeMap.splice(0, 0, [one]); - rangeMap.splice(1, 0, [two]); - rangeMap.splice(2, 0, [three]); - assert.equal(rangeMap.positionAt(0), 0); - assert.equal(rangeMap.positionAt(1), 1); - assert.equal(rangeMap.positionAt(2), 3); - - rangeMap.insertWhitespace(0, 2); - assert.equal(rangeMap.positionAt(0), 0); - assert.equal(rangeMap.positionAt(1), 3); - assert.equal(rangeMap.positionAt(2), 5); - }); - - - test('update whitespace when range map splices', () => { - rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); - - rangeMap.insertWhitespace(1, 2); - rangeMap.insertWhitespace(5, 3); - assert.equal(rangeMap.positionAt(0), 0); - assert.equal(rangeMap.positionAt(1), 1); - assert.equal(rangeMap.positionAt(2), 4); - assert.equal(rangeMap.positionAt(3), 5); - assert.equal(rangeMap.positionAt(4), 6); - assert.equal(rangeMap.positionAt(5), 7); - assert.equal(rangeMap.positionAt(6), 11); - assert.equal(rangeMap.positionAt(7), 12); - assert.equal(rangeMap.positionAt(8), 13); - - assert.equal(rangeMap.indexAt(0), 0); - assert.equal(rangeMap.indexAt(1), 1); - assert.equal(rangeMap.indexAt(2), 1); - assert.equal(rangeMap.indexAt(3), 1); - assert.equal(rangeMap.indexAt(4), 2); - - rangeMap.splice(1, 2); - assert.equal(rangeMap.positionAt(0), 0); - assert.equal(rangeMap.positionAt(1), 1); - assert.equal(rangeMap.positionAt(2), 2); - assert.equal(rangeMap.positionAt(3), 3); - assert.equal(rangeMap.positionAt(4), 7); - assert.equal(rangeMap.positionAt(5), 8); - assert.equal(rangeMap.positionAt(6), 9); - - assert.equal(rangeMap.indexAt(0), 0); - assert.equal(rangeMap.indexAt(1), 1); - assert.equal(rangeMap.indexAt(2), 2); - assert.equal(rangeMap.indexAt(3), 3); - assert.equal(rangeMap.indexAt(4), 3); - }); - }); }); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts index e3a815104a5..8e093bb690e 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts @@ -24,7 +24,6 @@ import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewMod import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; import { FileService } from 'vs/platform/files/common/fileService'; import { IFileService } from 'vs/platform/files/common/files'; -import { DiffComputer } from 'vs/editor/common/diff/diffComputer'; export class NotebookDiffEditor extends BaseEditor { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 5fbd4dc1e41..b03dfcc5622 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -60,7 +60,6 @@ import { isMacintosh, isNative } from 'vs/base/common/platform'; import { getTitleBarStyle } from 'vs/platform/windows/common/windows'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { CellDragAndDropController } from 'vs/workbench/contrib/notebook/browser/view/renderers/dnd'; -import { RangeMapWithWhitespace } from 'vs/workbench/contrib/notebook/browser/view/rangeMapWithWhitespace'; const $ = DOM.$; @@ -414,9 +413,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor enableKeyboardNavigation: true, additionalScrollHeight: 0, transformOptimization: (isMacintosh && isNative) || getTitleBarStyle(this.configurationService, this.environmentService) === 'native', - rangeMapProvider: () => { - return new RangeMapWithWhitespace(); - }, styleController: (_suffix: string) => { return this._list!; }, overrideStyles: { listBackground: editorBackground, diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index b7d4c726494..5f1693c9eba 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -28,6 +28,7 @@ import { SCROLLABLE_ELEMENT_PADDING_TOP } from 'vs/workbench/contrib/notebook/br import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { editorBackground, foreground, focusBorder } from 'vs/platform/theme/common/colorRegistry'; import { NotebookGutter, NotebookGutterDelegate, GutterRenderer } from 'vs/workbench/contrib/notebook/browser/view/notebookGutter'; +import { RangeMapWithWhitespace } from 'vs/workbench/contrib/notebook/browser/view/rangeMapWithWhitespace'; export interface IFocusNextPreviousDelegate { onFocusNext(applyFocusNext: () => void): void; @@ -67,6 +68,7 @@ export class NotebookCellList extends WorkbenchList implements ID private readonly _focusNextPreviousDelegate: IFocusNextPreviousDelegate; private _cellListGutter: WorkbenchList; + private _rangeMap = new RangeMapWithWhitespace(); constructor( private listUser: string, @@ -82,7 +84,12 @@ export class NotebookCellList extends WorkbenchList implements ID @IKeybindingService keybindingService: IKeybindingService, @IInstantiationService instantiationService: IInstantiationService ) { - super(listUser, container, delegate, renderers, options, contextKeyService, listService, themeService, configurationService, keybindingService); + super(listUser, container, delegate, renderers, { + ...options, + rangeMapProvider: () => { + return this._rangeMap; + } + }, contextKeyService, listService, themeService, configurationService, keybindingService); NOTEBOOK_CELL_LIST_FOCUSED.bindTo(this.contextKeyService).set(true); this._focusNextPreviousDelegate = options.focusNextPreviousDelegate; this._previousFocusedElements = this.getFocusedElements(); @@ -645,11 +652,13 @@ export class NotebookCellList extends WorkbenchList implements ID } insertWhitespace(index: number, height: number) { - this.view.insertWhitespace(index, height); + this._rangeMap.insertWhitespace(index, height); + this.view.rerender(); } updateWhitespace(index: number, newHeight: number) { - this.view.updateWhitespace(index, newHeight); + this._rangeMap.updateWhitespace(index, newHeight); + this.view.rerender(); } updateElementHeight2(element: ICellViewModel, size: number): void { diff --git a/src/vs/workbench/contrib/notebook/browser/view/rangeMapWithWhitespace.ts b/src/vs/workbench/contrib/notebook/browser/view/rangeMapWithWhitespace.ts index bf2adb1b1a0..2efe4eac2ed 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/rangeMapWithWhitespace.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/rangeMapWithWhitespace.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { concat, groupIntersect, IItem, IRangedGroup, shift } from 'vs/base/browser/ui/list/rangeMap'; +import { consolidate, groupIntersect, IItem, IRangedGroup, RangeMap, shift } from 'vs/base/browser/ui/list/rangeMap'; export class ListWhitespace { @@ -15,17 +15,25 @@ export class ListWhitespace { ) { } } -// [ { start: 0, len: 2, size: 2 }, { start: 2, len: 1, size: 3 }, {} ] -export class RangeMapWithWhitespace { +function concat(...groups: IRangedGroup[][]): IRangedGroup[] { + return consolidate(groups.reduce((r, g) => r.concat(g), [])); +} - private groups: IRangedGroup[] = []; +// [ { start: 0, len: 2, size: 2 }, { start: 2, len: 1, size: 3 }, {} ] +export class RangeMapWithWhitespace extends RangeMap { + + private rangeGroups: IRangedGroup[] = []; private whitespaces: ListWhitespace[] = []; - private _size = 0; + private _mapSize = 0; + + constructor() { + super(); + } splice(index: number, deleteCount: number, items: IItem[] = []): void { const diff = items.length - deleteCount; - const before = groupIntersect({ start: 0, end: index }, this.groups); - const after = groupIntersect({ start: index + deleteCount, end: Number.POSITIVE_INFINITY }, this.groups) + const before = groupIntersect({ start: 0, end: index }, this.rangeGroups); + const after = groupIntersect({ start: index + deleteCount, end: Number.POSITIVE_INFINITY }, this.rangeGroups) .map(g => ({ range: shift(g.range, diff), size: g.size })); const middle = items.map((item, i) => ({ @@ -33,8 +41,8 @@ export class RangeMapWithWhitespace { size: item.size })); - this.groups = concat(before, middle, after); - this._size = this.groups.reduce((t, g) => t + (g.size * (g.range.end - g.range.start)), 0); + this.rangeGroups = concat(before, middle, after); + this._mapSize = this.rangeGroups.reduce((t, g) => t + (g.size * (g.range.end - g.range.start)), 0); const deleteRange = deleteCount > 0 ? [index, index + deleteCount - 1] : []; const indexDelta = items.length - deleteCount; @@ -121,20 +129,20 @@ export class RangeMapWithWhitespace { * Returns the number of items in the range map. */ get count(): number { - const len = this.groups.length; + const len = this.rangeGroups.length; if (!len) { return 0; } - return this.groups[len - 1].range.end; + return this.rangeGroups[len - 1].range.end; } /** * Returns the sum of the sizes of all items in the range map. */ get size(): number { - return this._size + return this._mapSize + (this.whitespaces.length ? this.whitespaces[this.whitespaces.length - 1]?.prefixSum : 0); } @@ -183,7 +191,7 @@ export class RangeMapWithWhitespace { let index = 0; let size = 0; - for (let group of this.groups) { + for (let group of this.rangeGroups) { const count = group.range.end - group.range.start; const newSize = size + (count * group.size); @@ -230,7 +238,7 @@ export class RangeMapWithWhitespace { let position = 0; let count = 0; - for (let group of this.groups) { + for (let group of this.rangeGroups) { const groupCount = group.range.end - group.range.start; const newCount = count + groupCount; diff --git a/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts b/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts index d092389a66e..27e6ca1d5dc 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts @@ -8,6 +8,7 @@ import { NOTEBOOK_DISPLAY_ORDER, sortMimeTypes, CellKind, diff, CellUri } from ' import { TestCell, setupInstantiationService } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; import { URI } from 'vs/base/common/uri'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { RangeMapWithWhitespace } from 'vs/workbench/contrib/notebook/browser/view/rangeMapWithWhitespace'; suite('NotebookCommon', () => { const instantiationService = setupInstantiationService(); @@ -355,3 +356,172 @@ suite('CellUri', function () { assert.equal(actual?.notebook.toString(), nb.toString()); }); }); + +suite('RangeMap', () => { + let rangeMap: RangeMapWithWhitespace; + + setup(() => { + rangeMap = new RangeMapWithWhitespace(); + }); + + const one = { size: 1 }; + const two = { size: 2 }; + const three = { size: 3 }; + + test('insert whitespace 1', () => { + rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); + assert.equal(rangeMap.indexAt(0), 0); + assert.equal(rangeMap.indexAt(1), 1); + assert.equal(rangeMap.indexAt(5), 5); + assert.equal(rangeMap.indexAt(9), 9); + assert.equal(rangeMap.indexAt(10), 10); + assert.equal(rangeMap.indexAt(11), 10); + + for (let i = 0; i < 10; i++) { + rangeMap.insertWhitespace(i, 1); + } + + assert.equal(rangeMap.indexAt(0), 0); + assert.equal(rangeMap.indexAt(1), 0); + assert.equal(rangeMap.indexAt(2), 1); + assert.equal(rangeMap.indexAt(3), 1); + assert.equal(rangeMap.indexAt(4), 2); + assert.equal(rangeMap.indexAt(5), 2); + assert.equal(rangeMap.indexAt(6), 3); + assert.equal(rangeMap.indexAt(7), 3); + assert.equal(rangeMap.indexAt(8), 4); + assert.equal(rangeMap.indexAt(9), 4); + }); + + test('insert whitespace 2', () => { + rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); + assert.equal(rangeMap.indexAt(0), 0); + assert.equal(rangeMap.indexAt(1), 1); + assert.equal(rangeMap.indexAt(5), 5); + assert.equal(rangeMap.indexAt(9), 9); + assert.equal(rangeMap.indexAt(10), 10); + assert.equal(rangeMap.indexAt(11), 10); + + rangeMap.insertWhitespace(0, 2); + assert.equal(rangeMap.indexAt(0), 0); + assert.equal(rangeMap.indexAt(1), 0); + assert.equal(rangeMap.indexAt(2), 0); + assert.equal(rangeMap.indexAt(3), 1); + assert.equal(rangeMap.indexAt(4), 2); + assert.equal(rangeMap.indexAt(5), 3); + assert.equal(rangeMap.indexAt(10), 8); + + rangeMap.insertWhitespace(3, 3); + assert.equal(rangeMap.indexAt(0), 0); + assert.equal(rangeMap.indexAt(1), 0); + assert.equal(rangeMap.indexAt(2), 0); + assert.equal(rangeMap.indexAt(3), 1); + assert.equal(rangeMap.indexAt(4), 2); + assert.equal(rangeMap.indexAt(5), 3); + assert.equal(rangeMap.indexAt(6), 3); + assert.equal(rangeMap.indexAt(7), 3); + assert.equal(rangeMap.indexAt(8), 3); + assert.equal(rangeMap.indexAt(9), 4); + assert.equal(rangeMap.indexAt(10), 5); + }); + + test('insert whitespace 3', () => { + rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); + assert.equal(rangeMap.indexAt(0), 0); + assert.equal(rangeMap.indexAt(1), 1); + assert.equal(rangeMap.indexAt(5), 5); + assert.equal(rangeMap.indexAt(9), 9); + assert.equal(rangeMap.indexAt(10), 10); + assert.equal(rangeMap.indexAt(11), 10); + + rangeMap.insertWhitespace(0, 2); + assert.equal(rangeMap.indexAt(0), 0); + assert.equal(rangeMap.indexAt(1), 0); + assert.equal(rangeMap.indexAt(2), 0); + assert.equal(rangeMap.indexAt(3), 1); + assert.equal(rangeMap.indexAt(4), 2); + assert.equal(rangeMap.indexAt(5), 3); + assert.equal(rangeMap.indexAt(10), 8); + + rangeMap.insertWhitespace(3, 3); + rangeMap.updateWhitespace(0, 3); + assert.equal(rangeMap.indexAt(0), 0); + assert.equal(rangeMap.indexAt(1), 0); + assert.equal(rangeMap.indexAt(2), 0); + assert.equal(rangeMap.indexAt(3), 0); + assert.equal(rangeMap.indexAt(4), 1); + assert.equal(rangeMap.indexAt(5), 2); + assert.equal(rangeMap.indexAt(6), 3); + assert.equal(rangeMap.indexAt(7), 3); + assert.equal(rangeMap.indexAt(8), 3); + assert.equal(rangeMap.indexAt(9), 3); + assert.equal(rangeMap.indexAt(10), 4); + }); + + test('insert whitespace, positionAt', () => { + rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); + assert.equal(rangeMap.positionAt(0), 0); + assert.equal(rangeMap.positionAt(1), 1); + assert.equal(rangeMap.positionAt(5), 5); + assert.equal(rangeMap.positionAt(9), 9); + + rangeMap.insertWhitespace(0, 2); + assert.equal(rangeMap.positionAt(0), 0); + assert.equal(rangeMap.positionAt(1), 3); + assert.equal(rangeMap.positionAt(2), 4); + assert.equal(rangeMap.positionAt(3), 5); + }); + + test('insert whitespace, positionAt 2', () => { + rangeMap.splice(0, 0, [one]); + rangeMap.splice(1, 0, [two]); + rangeMap.splice(2, 0, [three]); + assert.equal(rangeMap.positionAt(0), 0); + assert.equal(rangeMap.positionAt(1), 1); + assert.equal(rangeMap.positionAt(2), 3); + + rangeMap.insertWhitespace(0, 2); + assert.equal(rangeMap.positionAt(0), 0); + assert.equal(rangeMap.positionAt(1), 3); + assert.equal(rangeMap.positionAt(2), 5); + }); + + + test('update whitespace when range map splices', () => { + rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); + + rangeMap.insertWhitespace(1, 2); + rangeMap.insertWhitespace(5, 3); + assert.equal(rangeMap.positionAt(0), 0); + assert.equal(rangeMap.positionAt(1), 1); + assert.equal(rangeMap.positionAt(2), 4); + assert.equal(rangeMap.positionAt(3), 5); + assert.equal(rangeMap.positionAt(4), 6); + assert.equal(rangeMap.positionAt(5), 7); + assert.equal(rangeMap.positionAt(6), 11); + assert.equal(rangeMap.positionAt(7), 12); + assert.equal(rangeMap.positionAt(8), 13); + + assert.equal(rangeMap.indexAt(0), 0); + assert.equal(rangeMap.indexAt(1), 1); + assert.equal(rangeMap.indexAt(2), 1); + assert.equal(rangeMap.indexAt(3), 1); + assert.equal(rangeMap.indexAt(4), 2); + + rangeMap.splice(1, 2); + assert.equal(rangeMap.positionAt(0), 0); + assert.equal(rangeMap.positionAt(1), 1); + assert.equal(rangeMap.positionAt(2), 2); + assert.equal(rangeMap.positionAt(3), 3); + assert.equal(rangeMap.positionAt(4), 7); + assert.equal(rangeMap.positionAt(5), 8); + assert.equal(rangeMap.positionAt(6), 9); + + assert.equal(rangeMap.indexAt(0), 0); + assert.equal(rangeMap.indexAt(1), 1); + assert.equal(rangeMap.indexAt(2), 2); + assert.equal(rangeMap.indexAt(3), 3); + assert.equal(rangeMap.indexAt(4), 3); + }); +}); + From 366edd28d5071e25c5bb84cb149d7e94adf1cf3c Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 17 Aug 2020 22:18:04 -0700 Subject: [PATCH 301/736] layer breaker --- .../contrib/notebook/browser/notebookEditorWidget.ts | 2 ++ .../contrib/notebook/browser/view/notebookCellList.ts | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index b03dfcc5622..51a7c9c3645 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -60,6 +60,7 @@ import { isMacintosh, isNative } from 'vs/base/common/platform'; import { getTitleBarStyle } from 'vs/platform/windows/common/windows'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { CellDragAndDropController } from 'vs/workbench/contrib/notebook/browser/view/renderers/dnd'; +import { RangeMapWithWhitespace } from 'vs/workbench/contrib/notebook/browser/view/rangeMapWithWhitespace'; const $ = DOM.$; @@ -442,6 +443,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor onFocusPrevious: (applyFocusPrevious: () => void) => this._updateForCursorNavigationMode(applyFocusPrevious), } }, + new RangeMapWithWhitespace() ); this._dndController.setList(this._list); diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index 5f1693c9eba..1238650deca 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -68,7 +68,7 @@ export class NotebookCellList extends WorkbenchList implements ID private readonly _focusNextPreviousDelegate: IFocusNextPreviousDelegate; private _cellListGutter: WorkbenchList; - private _rangeMap = new RangeMapWithWhitespace(); + private _rangeMap: RangeMapWithWhitespace; constructor( private listUser: string, @@ -78,6 +78,7 @@ export class NotebookCellList extends WorkbenchList implements ID renderers: IListRenderer[], contextKeyService: IContextKeyService, options: INotebookCellListOptions, + rangeMap: RangeMapWithWhitespace, @IListService listService: IListService, @IThemeService themeService: IThemeService, @IConfigurationService configurationService: IConfigurationService, @@ -87,9 +88,10 @@ export class NotebookCellList extends WorkbenchList implements ID super(listUser, container, delegate, renderers, { ...options, rangeMapProvider: () => { - return this._rangeMap; + return rangeMap; } }, contextKeyService, listService, themeService, configurationService, keybindingService); + this._rangeMap = rangeMap; NOTEBOOK_CELL_LIST_FOCUSED.bindTo(this.contextKeyService).set(true); this._focusNextPreviousDelegate = options.focusNextPreviousDelegate; this._previousFocusedElements = this.getFocusedElements(); From 3d5b2fecf2e6788cb9877d7d868d964fbc3ecd53 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Tue, 18 Aug 2020 01:30:56 -0400 Subject: [PATCH 302/736] Removes unused code --- .../contrib/timeline/common/timelineService.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/vs/workbench/contrib/timeline/common/timelineService.ts b/src/vs/workbench/contrib/timeline/common/timelineService.ts index 39a064a9a6f..1e6dde4eb70 100644 --- a/src/vs/workbench/contrib/timeline/common/timelineService.ts +++ b/src/vs/workbench/contrib/timeline/common/timelineService.ts @@ -27,7 +27,6 @@ export class TimelineService implements ITimelineService { private readonly _onDidChangeUri = new Emitter(); readonly onDidChangeUri: Event = this._onDidChangeUri.event; - private excludedSources: Set; private readonly hasProviderContext: IContextKey; private readonly providers = new Map(); private readonly providerSubscriptions = new Map(); @@ -39,16 +38,6 @@ export class TimelineService implements ITimelineService { @IContextKeyService protected contextKeyService: IContextKeyService, ) { this.hasProviderContext = TimelineHasProviderContext.bindTo(this.contextKeyService); - - this.excludedSources = new Set(configurationService.getValue('timeline.excludeSources')); - configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('timeline.excludeSources')) { - this.excludedSources = new Set(this.configurationService.getValue('timeline.excludeSources')); - - this.updateHasProviderContext(); - } - }, this); - this.updateHasProviderContext(); // let source = 'fast-source'; From 4491427ac73f863ce7fb7d1bf42b2443de25dc58 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 18 Aug 2020 09:18:23 +0200 Subject: [PATCH 303/736] web - move playground out of sources We will consume the playground from https://github.com/microsoft/vscode-web-playground going forward. --- build/lib/extensions.js | 1 - build/lib/extensions.ts | 1 - extensions/vscode-web-playground/.gitignore | 3 - .../vscode-web-playground/.vscode/tasks.json | 11 - .../vscode-web-playground/.vscodeignore | 11 - .../extension-browser.webpack.config.js | 18 - .../extension.webpack.config.js | 17 - extensions/vscode-web-playground/package.json | 106 - .../vscode-web-playground/src/exampleFiles.ts | 310 -- .../vscode-web-playground/src/extension.ts | 3927 ----------------- extensions/vscode-web-playground/src/memfs.ts | 449 -- .../src/typings/ref.d.ts | 10 - .../vscode-web-playground/tsconfig.json | 14 - extensions/vscode-web-playground/yarn.lock | 109 - resources/serverless/code-web.js | 48 +- 15 files changed, 47 insertions(+), 4988 deletions(-) delete mode 100644 extensions/vscode-web-playground/.gitignore delete mode 100644 extensions/vscode-web-playground/.vscode/tasks.json delete mode 100644 extensions/vscode-web-playground/.vscodeignore delete mode 100644 extensions/vscode-web-playground/extension-browser.webpack.config.js delete mode 100644 extensions/vscode-web-playground/extension.webpack.config.js delete mode 100644 extensions/vscode-web-playground/package.json delete mode 100644 extensions/vscode-web-playground/src/exampleFiles.ts delete mode 100644 extensions/vscode-web-playground/src/extension.ts delete mode 100644 extensions/vscode-web-playground/src/memfs.ts delete mode 100644 extensions/vscode-web-playground/src/typings/ref.d.ts delete mode 100644 extensions/vscode-web-playground/tsconfig.json delete mode 100644 extensions/vscode-web-playground/yarn.lock diff --git a/build/lib/extensions.js b/build/lib/extensions.js index 9cc40c4e1be..fe0deffc6d0 100644 --- a/build/lib/extensions.js +++ b/build/lib/extensions.js @@ -224,7 +224,6 @@ function packageLocalExtensionsStream(forWeb) { const extensionName = path.basename(extensionPath); return { name: extensionName, path: extensionPath, manifestPath: absoluteManifestPath }; }) - .filter(({ name }) => (name === 'vscode-web-playground' ? forWeb : true)) // package vscode-web-playground only for web .filter(({ name }) => excludedExtensions.indexOf(name) === -1) .filter(({ name }) => builtInExtensions.every(b => b.name !== name)) .filter(({ manifestPath }) => (forWeb ? isWebExtension(require(manifestPath)) : true))); diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts index 7e529f17cb8..dac71c81479 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts @@ -275,7 +275,6 @@ export function packageLocalExtensionsStream(forWeb: boolean): Stream { const extensionName = path.basename(extensionPath); return { name: extensionName, path: extensionPath, manifestPath: absoluteManifestPath }; }) - .filter(({ name }) => (name === 'vscode-web-playground' ? forWeb : true)) // package vscode-web-playground only for web .filter(({ name }) => excludedExtensions.indexOf(name) === -1) .filter(({ name }) => builtInExtensions.every(b => b.name !== name)) .filter(({ manifestPath }) => (forWeb ? isWebExtension(require(manifestPath)) : true)) diff --git a/extensions/vscode-web-playground/.gitignore b/extensions/vscode-web-playground/.gitignore deleted file mode 100644 index c19bd94aaa7..00000000000 --- a/extensions/vscode-web-playground/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -dist -out -node_modules diff --git a/extensions/vscode-web-playground/.vscode/tasks.json b/extensions/vscode-web-playground/.vscode/tasks.json deleted file mode 100644 index 390a93a3a7f..00000000000 --- a/extensions/vscode-web-playground/.vscode/tasks.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": "2.0.0", - "command": "npm", - "type": "shell", - "presentation": { - "reveal": "silent" - }, - "args": ["run", "compile"], - "isBackground": true, - "problemMatcher": "$tsc-watch" -} diff --git a/extensions/vscode-web-playground/.vscodeignore b/extensions/vscode-web-playground/.vscodeignore deleted file mode 100644 index 32fe3f03697..00000000000 --- a/extensions/vscode-web-playground/.vscodeignore +++ /dev/null @@ -1,11 +0,0 @@ -.vscode/** -build/** -dist/** -out/** -src/** -typings/** -.gitignore -extension-browser.webpack.config.js -extension.webpack.config.js -tsconfig.json -yarn.lock diff --git a/extensions/vscode-web-playground/extension-browser.webpack.config.js b/extensions/vscode-web-playground/extension-browser.webpack.config.js deleted file mode 100644 index dfd50aeff96..00000000000 --- a/extensions/vscode-web-playground/extension-browser.webpack.config.js +++ /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. - *--------------------------------------------------------------------------------------------*/ - -//@ts-check - -'use strict'; -const path = require('path'); -const withBrowserDefaults = require('../shared.webpack.config').browser; - -module.exports = withBrowserDefaults({ - context: __dirname, - node: false, - entry: { - extension: './src/extension.ts', - } -}); diff --git a/extensions/vscode-web-playground/extension.webpack.config.js b/extensions/vscode-web-playground/extension.webpack.config.js deleted file mode 100644 index 45600607fc5..00000000000 --- a/extensions/vscode-web-playground/extension.webpack.config.js +++ /dev/null @@ -1,17 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -//@ts-check - -'use strict'; - -const withDefaults = require('../shared.webpack.config'); - -module.exports = withDefaults({ - context: __dirname, - entry: { - extension: './src/extension.ts' - } -}); diff --git a/extensions/vscode-web-playground/package.json b/extensions/vscode-web-playground/package.json deleted file mode 100644 index 67ebd70edd4..00000000000 --- a/extensions/vscode-web-playground/package.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "name": "vscode-web-playground", - "description": "Web playground for VS Code", - "version": "0.0.1", - "publisher": "vscode", - "license": "MIT", - "enableProposedApi": true, - "private": true, - "activationEvents": [ - "onFileSystem:memfs", - "onDebug" - ], - "browser": "./dist/browser/extension", - "main": "./out/extension", - "engines": { - "vscode": "^1.25.0" - }, - "contributes": { - "taskDefinitions": [ - { - "type": "custombuildscript", - "required": [ - "flavor" - ], - "properties": { - "flavor": { - "type": "string", - "description": "The build flavor. Should be either '32' or '64'." - }, - "flags": { - "type": "array", - "description": "Additional build flags." - } - } - } - ], - "breakpoints": [ - { - "language": "markdown" - } - ], - "debuggers": [ - { - "type": "mock", - "label": "Mock Debug", - "languages": [ - "markdown" - ], - "configurationAttributes": { - "launch": { - "required": [ - "program" - ], - "properties": { - "program": { - "type": "string", - "description": "Absolute path to a text file.", - "default": "${workspaceFolder}/file.md" - }, - "stopOnEntry": { - "type": "boolean", - "description": "Automatically stop after launch.", - "default": true - }, - "trace": { - "type": "boolean", - "description": "Enable logging of the Debug Adapter Protocol.", - "default": true - } - } - } - }, - "initialConfigurations": [ - { - "type": "mock", - "request": "launch", - "name": "Debug file.md", - "program": "${workspaceFolder}/file.md" - } - ] - } - ], - "resourceLabelFormatters": [ - { - "scheme": "github", - "authority": "*", - "formatting": { - "label": "${authority}${path}", - "separator": "/", - "workspaceSuffix": "GitHub" - } - } - ] - }, - "scripts": { - "compile": "node ./node_modules/vscode/bin/compile -watch -p ./", - "compile-web": "npx webpack-cli --config extension.webpack.config --mode none", - "watch-web": "npx webpack-cli --config extension.webpack.config --mode none --watch --info-verbosity verbose", - "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-web-playground ./tsconfig.json" - }, - "devDependencies": { - "@types/mocha": "2.2.43", - "mocha-junit-reporter": "^1.17.0", - "mocha-multi-reporters": "^1.1.7" - } -} diff --git a/extensions/vscode-web-playground/src/exampleFiles.ts b/extensions/vscode-web-playground/src/exampleFiles.ts deleted file mode 100644 index a385f7b72e7..00000000000 --- a/extensions/vscode-web-playground/src/exampleFiles.ts +++ /dev/null @@ -1,310 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export const largeTSFile = `/// -/// - -module Mankala { -export var storeHouses = [6,13]; -export var svgNS = 'http://www.w3.org/2000/svg'; - -function createSVGRect(r:Rectangle) { - var rect = document.createElementNS(svgNS,'rect'); - rect.setAttribute('x', r.x.toString()); - rect.setAttribute('y', r.y.toString()); - rect.setAttribute('width', r.width.toString()); - rect.setAttribute('height', r.height.toString()); - return rect; -} - -function createSVGEllipse(r:Rectangle) { - var ell = document.createElementNS(svgNS,'ellipse'); - ell.setAttribute('rx',(r.width/2).toString()); - ell.setAttribute('ry',(r.height/2).toString()); - ell.setAttribute('cx',(r.x+r.width/2).toString()); - ell.setAttribute('cy',(r.y+r.height/2).toString()); - return ell; -} - -function createSVGEllipsePolar(angle:number,radius:number,tx:number,ty:number,cxo:number,cyo:number) { - var ell = document.createElementNS(svgNS,'ellipse'); - ell.setAttribute('rx',radius.toString()); - ell.setAttribute('ry',(radius/3).toString()); - ell.setAttribute('cx',cxo.toString()); - ell.setAttribute('cy',cyo.toString()); - var dangle = angle*(180/Math.PI); - ell.setAttribute('transform','rotate('+dangle+','+cxo+','+cyo+') translate('+tx+','+ty+')'); - return ell; -} - -function createSVGInscribedCircle(sq:Square) { - var circle = document.createElementNS(svgNS,'circle'); - circle.setAttribute('r',(sq.length/2).toString()); - circle.setAttribute('cx',(sq.x+(sq.length/2)).toString()); - circle.setAttribute('cy',(sq.y+(sq.length/2)).toString()); - return circle; -} - -export class Position { - - seedCounts:number[]; - startMove:number; - turn:number; - - constructor(seedCounts:number[],startMove:number,turn:number) { - this.seedCounts = seedCounts; - this.startMove = startMove; - this.turn = turn; - } - - score() { - var baseScore = this.seedCounts[storeHouses[1-this.turn]]-this.seedCounts[storeHouses[this.turn]]; - var otherSpaces = homeSpaces[this.turn]; - var sum = 0; - for (var k = 0,len = otherSpaces.length;k0) { - features.clear(); - var len = this.seedCounts.length; - for (var i = 0;i0) { - if (nextSpace==storeHouses[this.turn]) { - features.seedStoredCount++; - } - if ((nextSpace!=storeHouses[1-this.turn])) { - nextSeedCounts[nextSpace]++; - seedCount--; - } - if (seedCount==0) { - if (nextSpace==storeHouses[this.turn]) { - features.turnContinues = true; - } - else { - if ((nextSeedCounts[nextSpace]==1)&& - (nextSpace>=firstHomeSpace[this.turn])&& - (nextSpace<=lastHomeSpace[this.turn])) { - // capture - var capturedSpace = capturedSpaces[nextSpace]; - if (capturedSpace>=0) { - features.spaceCaptured = capturedSpace; - features.capturedCount = nextSeedCounts[capturedSpace]; - nextSeedCounts[capturedSpace] = 0; - nextSeedCounts[storeHouses[this.turn]] += features.capturedCount; - features.seedStoredCount += nextSeedCounts[capturedSpace]; - } - } - } - } - nextSpace = (nextSpace+1)%14; - } - return true; - } - else { - return false; - } - } -} - -export class SeedCoords { - tx:number; - ty:number; - angle:number; - - constructor(tx:number, ty:number, angle:number) { - this.tx = tx; - this.ty = ty; - this.angle = angle; - } -} - -export class DisplayPosition extends Position { - - config:SeedCoords[][]; - - constructor(seedCounts:number[],startMove:number,turn:number) { - super(seedCounts,startMove,turn); - - this.config = []; - - for (var i = 0;i(); - } - } - - - seedCircleRect(rect:Rectangle,seedCount:number,board:Element,seed:number) { - var coords = this.config[seed]; - var sq = rect.inner(0.95).square(); - var cxo = (sq.width/2)+sq.x; - var cyo = (sq.height/2)+sq.y; - var seedNumbers = [5,7,9,11]; - var ringIndex = 0; - var ringRem = seedNumbers[ringIndex]; - var angleDelta = (2*Math.PI)/ringRem; - var angle = angleDelta; - var seedLength = sq.width/(seedNumbers.length<<1); - var crMax = sq.width/2-(seedLength/2); - var pit = createSVGInscribedCircle(sq); - if (seed<7) { - pit.setAttribute('fill','brown'); - } - else { - pit.setAttribute('fill','saddlebrown'); - } - board.appendChild(pit); - var seedsSeen = 0; - while (seedCount > 0) { - if (ringRem == 0) { - ringIndex++; - ringRem = seedNumbers[ringIndex]; - angleDelta = (2*Math.PI)/ringRem; - angle = angleDelta; - } - var tx:number; - var ty:number; - var tangle = angle; - if (coords.length>seedsSeen) { - tx = coords[seedsSeen].tx; - ty = coords[seedsSeen].ty; - tangle = coords[seedsSeen].angle; - } - else { - tx = (Math.random()*crMax)-(crMax/3); - ty = (Math.random()*crMax)-(crMax/3); - coords[seedsSeen] = new SeedCoords(tx,ty,angle); - } - var ell = createSVGEllipsePolar(tangle,seedLength,tx,ty,cxo,cyo); - board.appendChild(ell); - angle += angleDelta; - ringRem--; - seedCount--; - seedsSeen++; - } - } - - toCircleSVG() { - var seedDivisions = 14; - var board = document.createElementNS(svgNS,'svg'); - var boardRect = new Rectangle(0,0,1800,800); - board.setAttribute('width','1800'); - board.setAttribute('height','800'); - var whole = createSVGRect(boardRect); - whole.setAttribute('fill','tan'); - board.appendChild(whole); - var labPlayLab = boardRect.proportionalSplitVert(20,760,20); - var playSurface = labPlayLab[1]; - var storeMainStore = playSurface.proportionalSplitHoriz(8,48,8); - var mainPair = storeMainStore[1].subDivideVert(2); - var playerRects = [mainPair[0].subDivideHoriz(6), mainPair[1].subDivideHoriz(6)]; - // reverse top layer because storehouse on left - for (var k = 0;k<3;k++) { - var temp = playerRects[0][k]; - playerRects[0][k] = playerRects[0][5-k]; - playerRects[0][5-k] = temp; - } - var storehouses = [storeMainStore[0],storeMainStore[2]]; - var playerSeeds = this.seedCounts.length>>1; - for (var i = 0;i<2;i++) { - var player = playerRects[i]; - var storehouse = storehouses[i]; - var r:Rectangle; - for (var j = 0;j(); - } - } - } - return board; - } -} -} -`; - -export const debuggableFile = `# VS Code Mock Debug - -This is a starter sample for developing VS Code debug adapters. - -**Mock Debug** simulates a debug adapter for Visual Studio Code. -It supports *step*, *continue*, *breakpoints*, *exceptions*, and -*variable access* but it is not connected to any real debugger. - -The sample is meant as an educational piece showing how to implement a debug -adapter for VS Code. It can be used as a starting point for developing a real adapter. - -More information about how to develop a new debug adapter can be found -[here](https://code.visualstudio.com/docs/extensions/example-debuggers). -Or discuss debug adapters on Gitter: -[![Gitter Chat](https://img.shields.io/badge/chat-online-brightgreen.svg)](https://gitter.im/Microsoft/vscode) - -## Using Mock Debug - -* Install the **Mock Debug** extension in VS Code. -* Create a new 'program' file 'readme.md' and enter several lines of arbitrary text. -* Switch to the debug viewlet and press the gear dropdown. -* Select the debug environment "Mock Debug". -* Press the green 'play' button to start debugging. - -You can now 'step through' the 'readme.md' file, set and hit breakpoints, and run into exceptions (if the word exception appears in a line). - -![Mock Debug](file.jpg) - -## Build and Run - -[![build status](https://travis-ci.org/Microsoft/vscode-mock-debug.svg?branch=master)](https://travis-ci.org/Microsoft/vscode-mock-debug) -[![build status](https://ci.appveyor.com/api/projects/status/empmw5q1tk6h1fly/branch/master?svg=true)](https://ci.appveyor.com/project/weinand/vscode-mock-debug) - - -* Clone the project [https://github.com/Microsoft/vscode-mock-debug.git](https://github.com/Microsoft/vscode-mock-debug.git) -* Open the project folder in VS Code. -* Press 'F5' to build and launch Mock Debug in another VS Code window. In that window: -* Open a new workspace, create a new 'program' file 'readme.md' and enter several lines of arbitrary text. -* Switch to the debug viewlet and press the gear dropdown. -* Select the debug environment "Mock Debug". -* Press 'F5' to start debugging.`; - -export function getImageFile(): Uint8Array { - const data = atob(`/9j/4AAQSkZJRgABAQAASABIAAD/2wCEAA4ODg4ODhcODhchFxcXIS0hISEhLTktLS0tLTlFOTk5OTk5RUVFRUVFRUVSUlJSUlJgYGBgYGxsbGxsbGxsbGwBERISGxkbLxkZL3FMP0xxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcf/AABEIAFYAZAMBIgACEQEDEQH/xAB1AAACAwEBAQAAAAAAAAAAAAAABAMFBgIBBxAAAgIBAwMCBQQCAwAAAAAAAQIAAxEEBSESMUFRcRMiIzJhFIGRoQbBQlKxAQEBAQEAAAAAAAAAAAAAAAABAgADEQEBAQADAQEAAAAAAAAAAAAAARESITECQf/aAAwDAQACEQMRAD8A2LEZkLc/bKxbdYEHWoyfEze56zXpqRTTYUyPHiVrY2TVZyMzhFZMg8iYE6jcVXAusY98KMnj2lhRu+4aLoGuTNTYPV5APnyDNyPFp6EY3EsO3kxnVVLZVg8z2tw9YsXkGQpcbGIbxHQzep0vw8Jgc8n28CJJRY30lBwzf1iaa2ku/HmMV01VW/k/6hh0abTDTafpPcTytmckEewjeosAqJEj0yDo6yO/rFLzoGME5nIAXtGSM9uwnjLn8zFECw7QneITMWouR7gj9/Ep94061bjXa32WDGfzOGuCXKy9/wDc0FlFe5aX4OpHJHBHcSfT4w246bWJar6MsCwKnp9DOF0r6XRiu5snvg9hNK217vQeih0tXwzcED895R7voNfWoN9gOT2QH/2T3mHrda3Y+p9ppZuSV/qR0j6r+5ju2oun2ypOwCAASGikISzdySf5lxLsAdRPpIqw91xC/wDHvGbAAh88RnSVCjT9b8E/MYsguerTqWuYKo8k4ESTcttsPSmoQ+zCZPWPbvWqsvLE0IxCL4wPP7xEW7TXeKsvaGABOMdLef2ky7ejevX0tBWy5Qhh6jmS9IIxPm6XazbW69K56M/aeRibnSaqyytWtGCfE0+tazDhrHpCdixT5EJSWD1BPkcjsYxpN21FWEcdu0dG3hl8rIX0YqUgDqkSrq/0+6oyfOOZT7hqxqLMKMk8ARfS0fqGatAR04yCY+u3OpLt38e0rQl0tzsFrc8rxj0lqqDHMzujIXUMGPI4mjS1MTCvG8gRLddYE2811n5nHTJ9RaAsztzZ1AZhlX9fBi0VWgWzbSqahfpWfa/iSnatMuqOpVgVPIHGMzc6erS3aQVOoZSMFTK19i2pTwGA9Axx/E58b+K2M8lP6/Urp6BkA5Y+OPE112nrIFeOw8RMajQ7dWU0iAH8TyrVG0mw8EypMFuk7K9TS5RGJHiEYsuUtmEWO1KO2RGDRSVJzj1MiQhOQIx8QEYK5hGpUUJVc1lTgcDjEe1FPxqGQHBZSMiQqa8/Z38xgOoHB/aIfJNVZrdFqirsVbsfzLXT7+UQLYmcDHBlh/k+g+KP1dOCV+4efcTNbdtGq3CxQiMKyeX7CGqxqtDuK7lYK2BXnAz3JMuNZoPpDAyV5zHNt2bRbcA1S/Pjljyf7jerWxx0V4wQeZgynxrUXoUnIif629GJY595cptr1N9XJYjOfEi1G3LYMLgH1m04qxelrAtnj/qZYIvUPpMcHwYtTT8FzVaMN6+sslqVF6gcQ1sRivPccwjS314+bGYRBnqzws6FhUfL7CQ8gdI7+TDIHHgcSVGBYRznMXfUL2J5ngPUOYCpfM2tiq1tnUpVRnMe0DGtAKyQIw+mU4GJCKmrPy+I6V0lxYYIzxOCtdjZyVIMRqtPsYx8RT37+sdRhsFlHzcyC0J0kmcfqFX5cxC7VAk4OPUQtM+UVtYf7vH8iKP8SnKg5U9xHQwsGV7jxF9QnWACMEcgwlUjT4ZUE+YRRLGRehwciEpLRMAAT6SALlIQkF4kl7HEIQLwuQfac9RPeEJi5H3TruvvmEJo1QOcgGQuvVg+sITM8rDKeDHVItXkQhKgqM6esnJEIQlJf//Z`); - return Uint8Array.from([...data].map(x => x.charCodeAt(0))); -} - -// encoded from 'АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюя' -export const windows1251File = Uint8Array.from([192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255]); - -// encoded from '中国abc' -export const gbkFile = Uint8Array.from([214, 208, 185, 250, 97, 98, 99]); diff --git a/extensions/vscode-web-playground/src/extension.ts b/extensions/vscode-web-playground/src/extension.ts deleted file mode 100644 index 1d5df92bbcc..00000000000 --- a/extensions/vscode-web-playground/src/extension.ts +++ /dev/null @@ -1,3927 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -// -// ############################################################################ -// -// ! USED FOR RUNNING VSCODE OUT OF SOURCES FOR WEB ! -// ! DO NOT REMOVE ! -// -// ############################################################################ -// - -import * as vscode from 'vscode'; -import { MemFS } from './memfs'; - -declare const navigator: unknown; - -export function activate(context: vscode.ExtensionContext) { - if (typeof navigator === 'object') { // do not run under node.js - const memFs = enableFs(context); - - if (vscode.workspace.workspaceFolders?.some(f => f.uri.scheme === MemFS.scheme)) { - memFs.seed(); - enableProblems(context); - enableTasks(); - enableDebug(context, memFs); - - vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(`memfs:/sample-folder/large.ts`)); - } - } -} - -function enableFs(context: vscode.ExtensionContext): MemFS { - const memFs = new MemFS(); - context.subscriptions.push(memFs); - - return memFs; -} - -function enableProblems(context: vscode.ExtensionContext): void { - const collection = vscode.languages.createDiagnosticCollection('test'); - if (vscode.window.activeTextEditor) { - updateDiagnostics(vscode.window.activeTextEditor.document, collection); - } - context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor(editor => { - if (editor) { - updateDiagnostics(editor.document, collection); - } - })); -} - -function updateDiagnostics(document: vscode.TextDocument, collection: vscode.DiagnosticCollection): void { - if (document && document.fileName === '/sample-folder/large.ts') { - collection.set(document.uri, [{ - code: '', - message: 'cannot assign twice to immutable variable `storeHouses`', - range: new vscode.Range(new vscode.Position(4, 12), new vscode.Position(4, 32)), - severity: vscode.DiagnosticSeverity.Error, - source: '', - relatedInformation: [ - new vscode.DiagnosticRelatedInformation(new vscode.Location(document.uri, new vscode.Range(new vscode.Position(1, 8), new vscode.Position(1, 9))), 'first assignment to `x`') - ] - }, { - code: '', - message: 'function does not follow naming conventions', - range: new vscode.Range(new vscode.Position(7, 10), new vscode.Position(7, 23)), - severity: vscode.DiagnosticSeverity.Warning, - source: '' - }]); - } else { - collection.clear(); - } -} - -function enableTasks(): void { - - interface CustomBuildTaskDefinition extends vscode.TaskDefinition { - /** - * The build flavor. Should be either '32' or '64'. - */ - flavor: string; - - /** - * Additional build flags - */ - flags?: string[]; - } - - class CustomBuildTaskProvider implements vscode.TaskProvider { - static CustomBuildScriptType: string = 'custombuildscript'; - private tasks: vscode.Task[] | undefined; - - // We use a CustomExecution task when state needs to be shared accross runs of the task or when - // the task requires use of some VS Code API to run. - // If you don't need to share state between runs and if you don't need to execute VS Code API in your task, - // then a simple ShellExecution or ProcessExecution should be enough. - // Since our build has this shared state, the CustomExecution is used below. - private sharedState: string | undefined; - - constructor(private workspaceRoot: string) { } - - public async provideTasks(): Promise { - return this.getTasks(); - } - - public resolveTask(_task: vscode.Task): vscode.Task | undefined { - const flavor: string = _task.definition.flavor; - if (flavor) { - const definition: CustomBuildTaskDefinition = _task.definition; - return this.getTask(definition.flavor, definition.flags ? definition.flags : [], definition); - } - return undefined; - } - - private getTasks(): vscode.Task[] { - if (this.tasks !== undefined) { - return this.tasks; - } - // In our fictional build, we have two build flavors - const flavors: string[] = ['32', '64']; - // Each flavor can have some options. - const flags: string[][] = [['watch', 'incremental'], ['incremental'], []]; - - this.tasks = []; - flavors.forEach(flavor => { - flags.forEach(flagGroup => { - this.tasks!.push(this.getTask(flavor, flagGroup)); - }); - }); - return this.tasks; - } - - private getTask(flavor: string, flags: string[], definition?: CustomBuildTaskDefinition): vscode.Task { - if (definition === undefined) { - definition = { - type: CustomBuildTaskProvider.CustomBuildScriptType, - flavor, - flags - }; - } - return new vscode.Task2(definition, vscode.TaskScope.Workspace, `${flavor} ${flags.join(' ')}`, - CustomBuildTaskProvider.CustomBuildScriptType, new vscode.CustomExecution(async (): Promise => { - // When the task is executed, this callback will run. Here, we setup for running the task. - return new CustomBuildTaskTerminal(this.workspaceRoot, flavor, flags, () => this.sharedState, (state: string) => this.sharedState = state); - })); - } - } - - class CustomBuildTaskTerminal implements vscode.Pseudoterminal { - private writeEmitter = new vscode.EventEmitter(); - onDidWrite: vscode.Event = this.writeEmitter.event; - private closeEmitter = new vscode.EventEmitter(); - onDidClose?: vscode.Event = this.closeEmitter.event; - - private fileWatcher: vscode.FileSystemWatcher | undefined; - - constructor(private workspaceRoot: string, _flavor: string, private flags: string[], private getSharedState: () => string | undefined, private setSharedState: (state: string) => void) { - } - - open(_initialDimensions: vscode.TerminalDimensions | undefined): void { - // At this point we can start using the terminal. - if (this.flags.indexOf('watch') > -1) { - let pattern = this.workspaceRoot + '/customBuildFile'; - this.fileWatcher = vscode.workspace.createFileSystemWatcher(pattern); - this.fileWatcher.onDidChange(() => this.doBuild()); - this.fileWatcher.onDidCreate(() => this.doBuild()); - this.fileWatcher.onDidDelete(() => this.doBuild()); - } - this.doBuild(); - } - - close(): void { - // The terminal has been closed. Shutdown the build. - if (this.fileWatcher) { - this.fileWatcher.dispose(); - } - } - - private async doBuild(): Promise { - return new Promise((resolve) => { - this.writeEmitter.fire('Starting build...\r\n'); - let isIncremental = this.flags.indexOf('incremental') > -1; - if (isIncremental) { - if (this.getSharedState()) { - this.writeEmitter.fire('Using last build results: ' + this.getSharedState() + '\r\n'); - } else { - isIncremental = false; - this.writeEmitter.fire('No result from last build. Doing full build.\r\n'); - } - } - - // Since we don't actually build anything in this example set a timeout instead. - setTimeout(() => { - const date = new Date(); - this.setSharedState(date.toTimeString() + ' ' + date.toDateString()); - this.writeEmitter.fire('Build complete.\r\n\r\n'); - if (this.flags.indexOf('watch') === -1) { - this.closeEmitter.fire(); - resolve(); - } - }, isIncremental ? 1000 : 4000); - }); - } - } - - vscode.tasks.registerTaskProvider(CustomBuildTaskProvider.CustomBuildScriptType, new CustomBuildTaskProvider(vscode.workspace.rootPath!)); -} - -//--------------------------------------------------------------------------- -// DEBUG -//--------------------------------------------------------------------------- - -function enableDebug(context: vscode.ExtensionContext, memFs: MemFS): void { - context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('mock', new MockConfigurationProvider())); - context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('mock', new MockDebugAdapterDescriptorFactory(memFs))); -} - -/** - * Declaration module describing the VS Code debug protocol. - * Auto-generated from json schema. Do not edit manually. - */ -declare module DebugProtocol { - - /** Base class of requests, responses, and events. */ - export interface ProtocolMessage { - /** Sequence number (also known as message ID). For protocol messages of type 'request' this ID can be used to cancel the request. */ - seq: number; - /** Message type. - Values: 'request', 'response', 'event', etc. - */ - type: string; - } - - /** A client or debug adapter initiated request. */ - export interface Request extends ProtocolMessage { - // type: 'request'; - /** The command to execute. */ - command: string; - /** Object containing arguments for the command. */ - arguments?: any; - } - - /** A debug adapter initiated event. */ - export interface Event extends ProtocolMessage { - // type: 'event'; - /** Type of event. */ - event: string; - /** Event-specific information. */ - body?: any; - } - - /** Response for a request. */ - export interface Response extends ProtocolMessage { - // type: 'response'; - /** Sequence number of the corresponding request. */ - request_seq: number; - /** Outcome of the request. - If true, the request was successful and the 'body' attribute may contain the result of the request. - If the value is false, the attribute 'message' contains the error in short form and the 'body' may contain additional information (see 'ErrorResponse.body.error'). - */ - success: boolean; - /** The command requested. */ - command: string; - /** Contains the raw error in short form if 'success' is false. - This raw error might be interpreted by the frontend and is not shown in the UI. - Some predefined values exist. - Values: - 'cancelled': request was cancelled. - etc. - */ - message?: string; - /** Contains request result if success is true and optional error details if success is false. */ - body?: any; - } - - /** On error (whenever 'success' is false), the body can provide more details. */ - export interface ErrorResponse extends Response { - body: { - /** An optional, structured error message. */ - error?: Message; - }; - } - - /** Cancel request; value of command field is 'cancel'. - The 'cancel' request is used by the frontend to indicate that it is no longer interested in the result produced by a specific request issued earlier. - This request has a hint characteristic: a debug adapter can only be expected to make a 'best effort' in honouring this request but there are no guarantees. - The 'cancel' request may return an error if it could not cancel an operation but a frontend should refrain from presenting this error to end users. - A frontend client should only call this request if the capability 'supportsCancelRequest' is true. - The request that got canceled still needs to send a response back. - This can either be a normal result ('success' attribute true) or an error response ('success' attribute false and the 'message' set to 'cancelled'). - Returning partial results from a cancelled request is possible but please note that a frontend client has no generic way for detecting that a response is partial or not. - */ - export interface CancelRequest extends Request { - // command: 'cancel'; - arguments?: CancelArguments; - } - - /** Arguments for 'cancel' request. */ - export interface CancelArguments { - /** The ID (attribute 'seq') of the request to cancel. */ - requestId?: number; - } - - /** Response to 'cancel' request. This is just an acknowledgement, so no body field is required. */ - export interface CancelResponse extends Response { - } - - /** Event message for 'initialized' event type. - This event indicates that the debug adapter is ready to accept configuration requests (e.g. SetBreakpointsRequest, SetExceptionBreakpointsRequest). - A debug adapter is expected to send this event when it is ready to accept configuration requests (but not before the 'initialize' request has finished). - The sequence of events/requests is as follows: - - adapters sends 'initialized' event (after the 'initialize' request has returned) - - frontend sends zero or more 'setBreakpoints' requests - - frontend sends one 'setFunctionBreakpoints' request - - frontend sends a 'setExceptionBreakpoints' request if one or more 'exceptionBreakpointFilters' have been defined (or if 'supportsConfigurationDoneRequest' is not defined or false) - - frontend sends other future configuration requests - - frontend sends one 'configurationDone' request to indicate the end of the configuration. - */ - export interface InitializedEvent extends Event { - // event: 'initialized'; - } - - /** Event message for 'stopped' event type. - The event indicates that the execution of the debuggee has stopped due to some condition. - This can be caused by a break point previously set, a stepping action has completed, by executing a debugger statement etc. - */ - export interface StoppedEvent extends Event { - // event: 'stopped'; - body: { - /** The reason for the event. - For backward compatibility this string is shown in the UI if the 'description' attribute is missing (but it must not be translated). - Values: 'step', 'breakpoint', 'exception', 'pause', 'entry', 'goto', 'function breakpoint', 'data breakpoint', etc. - */ - reason: string; - /** The full reason for the event, e.g. 'Paused on exception'. This string is shown in the UI as is and must be translated. */ - description?: string; - /** The thread which was stopped. */ - threadId?: number; - /** A value of true hints to the frontend that this event should not change the focus. */ - preserveFocusHint?: boolean; - /** Additional information. E.g. if reason is 'exception', text contains the exception name. This string is shown in the UI. */ - text?: string; - /** If 'allThreadsStopped' is true, a debug adapter can announce that all threads have stopped. - - The client should use this information to enable that all threads can be expanded to access their stacktraces. - - If the attribute is missing or false, only the thread with the given threadId can be expanded. - */ - allThreadsStopped?: boolean; - }; - } - - /** Event message for 'continued' event type. - The event indicates that the execution of the debuggee has continued. - Please note: a debug adapter is not expected to send this event in response to a request that implies that execution continues, e.g. 'launch' or 'continue'. - It is only necessary to send a 'continued' event if there was no previous request that implied this. - */ - export interface ContinuedEvent extends Event { - // event: 'continued'; - body: { - /** The thread which was continued. */ - threadId: number; - /** If 'allThreadsContinued' is true, a debug adapter can announce that all threads have continued. */ - allThreadsContinued?: boolean; - }; - } - - /** Event message for 'exited' event type. - The event indicates that the debuggee has exited and returns its exit code. - */ - export interface ExitedEvent extends Event { - // event: 'exited'; - body: { - /** The exit code returned from the debuggee. */ - exitCode: number; - }; - } - - /** Event message for 'terminated' event type. - The event indicates that debugging of the debuggee has terminated. This does **not** mean that the debuggee itself has exited. - */ - export interface TerminatedEvent extends Event { - // event: 'terminated'; - body?: { - /** A debug adapter may set 'restart' to true (or to an arbitrary object) to request that the front end restarts the session. - The value is not interpreted by the client and passed unmodified as an attribute '__restart' to the 'launch' and 'attach' requests. - */ - restart?: any; - }; - } - - /** Event message for 'thread' event type. - The event indicates that a thread has started or exited. - */ - export interface ThreadEvent extends Event { - // event: 'thread'; - body: { - /** The reason for the event. - Values: 'started', 'exited', etc. - */ - reason: string; - /** The identifier of the thread. */ - threadId: number; - }; - } - - /** Event message for 'output' event type. - The event indicates that the target has produced some output. - */ - export interface OutputEvent extends Event { - // event: 'output'; - body: { - /** The output category. If not specified, 'console' is assumed. - Values: 'console', 'stdout', 'stderr', 'telemetry', etc. - */ - category?: string; - /** The output to report. */ - output: string; - /** If an attribute 'variablesReference' exists and its value is > 0, the output contains objects which can be retrieved by passing 'variablesReference' to the 'variables' request. The value should be less than or equal to 2147483647 (2^31 - 1). */ - variablesReference?: number; - /** An optional source location where the output was produced. */ - source?: Source; - /** An optional source location line where the output was produced. */ - line?: number; - /** An optional source location column where the output was produced. */ - column?: number; - /** Optional data to report. For the 'telemetry' category the data will be sent to telemetry, for the other categories the data is shown in JSON format. */ - data?: any; - }; - } - - /** Event message for 'breakpoint' event type. - The event indicates that some information about a breakpoint has changed. - */ - export interface BreakpointEvent extends Event { - // event: 'breakpoint'; - body: { - /** The reason for the event. - Values: 'changed', 'new', 'removed', etc. - */ - reason: string; - /** The 'id' attribute is used to find the target breakpoint and the other attributes are used as the new values. */ - breakpoint: Breakpoint; - }; - } - - /** Event message for 'module' event type. - The event indicates that some information about a module has changed. - */ - export interface ModuleEvent extends Event { - // event: 'module'; - body: { - /** The reason for the event. */ - reason: 'new' | 'changed' | 'removed'; - /** The new, changed, or removed module. In case of 'removed' only the module id is used. */ - module: Module; - }; - } - - /** Event message for 'loadedSource' event type. - The event indicates that some source has been added, changed, or removed from the set of all loaded sources. - */ - export interface LoadedSourceEvent extends Event { - // event: 'loadedSource'; - body: { - /** The reason for the event. */ - reason: 'new' | 'changed' | 'removed'; - /** The new, changed, or removed source. */ - source: Source; - }; - } - - /** Event message for 'process' event type. - The event indicates that the debugger has begun debugging a new process. Either one that it has launched, or one that it has attached to. - */ - export interface ProcessEvent extends Event { - // event: 'process'; - body: { - /** The logical name of the process. This is usually the full path to process's executable file. Example: /home/example/myproj/program.js. */ - name: string; - /** The system process id of the debugged process. This property will be missing for non-system processes. */ - systemProcessId?: number; - /** If true, the process is running on the same computer as the debug adapter. */ - isLocalProcess?: boolean; - /** Describes how the debug engine started debugging this process. - 'launch': Process was launched under the debugger. - 'attach': Debugger attached to an existing process. - 'attachForSuspendedLaunch': A project launcher component has launched a new process in a suspended state and then asked the debugger to attach. - */ - startMethod?: 'launch' | 'attach' | 'attachForSuspendedLaunch'; - /** The size of a pointer or address for this process, in bits. This value may be used by clients when formatting addresses for display. */ - pointerSize?: number; - }; - } - - /** Event message for 'capabilities' event type. - The event indicates that one or more capabilities have changed. - Since the capabilities are dependent on the frontend and its UI, it might not be possible to change that at random times (or too late). - Consequently this event has a hint characteristic: a frontend can only be expected to make a 'best effort' in honouring individual capabilities but there are no guarantees. - Only changed capabilities need to be included, all other capabilities keep their values. - */ - export interface CapabilitiesEvent extends Event { - // event: 'capabilities'; - body: { - /** The set of updated capabilities. */ - capabilities: Capabilities; - }; - } - - /** RunInTerminal request; value of command field is 'runInTerminal'. - This request is sent from the debug adapter to the client to run a command in a terminal. This is typically used to launch the debuggee in a terminal provided by the client. - */ - export interface RunInTerminalRequest extends Request { - // command: 'runInTerminal'; - arguments: RunInTerminalRequestArguments; - } - - /** Arguments for 'runInTerminal' request. */ - export interface RunInTerminalRequestArguments { - /** What kind of terminal to launch. */ - kind?: 'integrated' | 'external'; - /** Optional title of the terminal. */ - title?: string; - /** Working directory of the command. */ - cwd: string; - /** List of arguments. The first argument is the command to run. */ - args: string[]; - /** Environment key-value pairs that are added to or removed from the default environment. */ - env?: { [key: string]: string | null; }; - } - - /** Response to 'runInTerminal' request. */ - export interface RunInTerminalResponse extends Response { - body: { - /** The process ID. The value should be less than or equal to 2147483647 (2^31 - 1). */ - processId?: number; - /** The process ID of the terminal shell. The value should be less than or equal to 2147483647 (2^31 - 1). */ - shellProcessId?: number; - }; - } - - /** Initialize request; value of command field is 'initialize'. - The 'initialize' request is sent as the first request from the client to the debug adapter in order to configure it with client capabilities and to retrieve capabilities from the debug adapter. - Until the debug adapter has responded to with an 'initialize' response, the client must not send any additional requests or events to the debug adapter. In addition the debug adapter is not allowed to send any requests or events to the client until it has responded with an 'initialize' response. - The 'initialize' request may only be sent once. - */ - export interface InitializeRequest extends Request { - // command: 'initialize'; - arguments: InitializeRequestArguments; - } - - /** Arguments for 'initialize' request. */ - export interface InitializeRequestArguments { - /** The ID of the (frontend) client using this adapter. */ - clientID?: string; - /** The human readable name of the (frontend) client using this adapter. */ - clientName?: string; - /** The ID of the debug adapter. */ - adapterID: string; - /** The ISO-639 locale of the (frontend) client using this adapter, e.g. en-US or de-CH. */ - locale?: string; - /** If true all line numbers are 1-based (default). */ - linesStartAt1?: boolean; - /** If true all column numbers are 1-based (default). */ - columnsStartAt1?: boolean; - /** Determines in what format paths are specified. The default is 'path', which is the native format. - Values: 'path', 'uri', etc. - */ - pathFormat?: string; - /** Client supports the optional type attribute for variables. */ - supportsVariableType?: boolean; - /** Client supports the paging of variables. */ - supportsVariablePaging?: boolean; - /** Client supports the runInTerminal request. */ - supportsRunInTerminalRequest?: boolean; - /** Client supports memory references. */ - supportsMemoryReferences?: boolean; - } - - /** Response to 'initialize' request. */ - export interface InitializeResponse extends Response { - /** The capabilities of this debug adapter. */ - body?: Capabilities; - } - - /** ConfigurationDone request; value of command field is 'configurationDone'. - The client of the debug protocol must send this request at the end of the sequence of configuration requests (which was started by the 'initialized' event). - */ - export interface ConfigurationDoneRequest extends Request { - // command: 'configurationDone'; - arguments?: ConfigurationDoneArguments; - } - - /** Arguments for 'configurationDone' request. */ - export interface ConfigurationDoneArguments { - } - - /** Response to 'configurationDone' request. This is just an acknowledgement, so no body field is required. */ - export interface ConfigurationDoneResponse extends Response { - } - - /** Launch request; value of command field is 'launch'. - The launch request is sent from the client to the debug adapter to start the debuggee with or without debugging (if 'noDebug' is true). Since launching is debugger/runtime specific, the arguments for this request are not part of this specification. - */ - export interface LaunchRequest extends Request { - // command: 'launch'; - arguments: LaunchRequestArguments; - } - - /** Arguments for 'launch' request. Additional attributes are implementation specific. */ - export interface LaunchRequestArguments { - /** If noDebug is true the launch request should launch the program without enabling debugging. */ - noDebug?: boolean; - /** Optional data from the previous, restarted session. - The data is sent as the 'restart' attribute of the 'terminated' event. - The client should leave the data intact. - */ - __restart?: any; - } - - /** Response to 'launch' request. This is just an acknowledgement, so no body field is required. */ - export interface LaunchResponse extends Response { - } - - /** Attach request; value of command field is 'attach'. - The attach request is sent from the client to the debug adapter to attach to a debuggee that is already running. Since attaching is debugger/runtime specific, the arguments for this request are not part of this specification. - */ - export interface AttachRequest extends Request { - // command: 'attach'; - arguments: AttachRequestArguments; - } - - /** Arguments for 'attach' request. Additional attributes are implementation specific. */ - export interface AttachRequestArguments { - /** Optional data from the previous, restarted session. - The data is sent as the 'restart' attribute of the 'terminated' event. - The client should leave the data intact. - */ - __restart?: any; - } - - /** Response to 'attach' request. This is just an acknowledgement, so no body field is required. */ - export interface AttachResponse extends Response { - } - - /** Restart request; value of command field is 'restart'. - Restarts a debug session. If the capability 'supportsRestartRequest' is missing or has the value false, - the client will implement 'restart' by terminating the debug adapter first and then launching it anew. - A debug adapter can override this default behaviour by implementing a restart request - and setting the capability 'supportsRestartRequest' to true. - */ - export interface RestartRequest extends Request { - // command: 'restart'; - arguments?: RestartArguments; - } - - /** Arguments for 'restart' request. */ - export interface RestartArguments { - } - - /** Response to 'restart' request. This is just an acknowledgement, so no body field is required. */ - export interface RestartResponse extends Response { - } - - /** Disconnect request; value of command field is 'disconnect'. - The 'disconnect' request is sent from the client to the debug adapter in order to stop debugging. It asks the debug adapter to disconnect from the debuggee and to terminate the debug adapter. If the debuggee has been started with the 'launch' request, the 'disconnect' request terminates the debuggee. If the 'attach' request was used to connect to the debuggee, 'disconnect' does not terminate the debuggee. This behavior can be controlled with the 'terminateDebuggee' argument (if supported by the debug adapter). - */ - export interface DisconnectRequest extends Request { - // command: 'disconnect'; - arguments?: DisconnectArguments; - } - - /** Arguments for 'disconnect' request. */ - export interface DisconnectArguments { - /** A value of true indicates that this 'disconnect' request is part of a restart sequence. */ - restart?: boolean; - /** Indicates whether the debuggee should be terminated when the debugger is disconnected. - If unspecified, the debug adapter is free to do whatever it thinks is best. - A client can only rely on this attribute being properly honored if a debug adapter returns true for the 'supportTerminateDebuggee' capability. - */ - terminateDebuggee?: boolean; - } - - /** Response to 'disconnect' request. This is just an acknowledgement, so no body field is required. */ - export interface DisconnectResponse extends Response { - } - - /** Terminate request; value of command field is 'terminate'. - The 'terminate' request is sent from the client to the debug adapter in order to give the debuggee a chance for terminating itself. - */ - export interface TerminateRequest extends Request { - // command: 'terminate'; - arguments?: TerminateArguments; - } - - /** Arguments for 'terminate' request. */ - export interface TerminateArguments { - /** A value of true indicates that this 'terminate' request is part of a restart sequence. */ - restart?: boolean; - } - - /** Response to 'terminate' request. This is just an acknowledgement, so no body field is required. */ - export interface TerminateResponse extends Response { - } - - /** BreakpointLocations request; value of command field is 'breakpointLocations'. - The 'breakpointLocations' request returns all possible locations for source breakpoints in a given range. - */ - export interface BreakpointLocationsRequest extends Request { - // command: 'breakpointLocations'; - arguments?: BreakpointLocationsArguments; - } - - /** Arguments for 'breakpointLocations' request. */ - export interface BreakpointLocationsArguments { - /** The source location of the breakpoints; either 'source.path' or 'source.reference' must be specified. */ - source: Source; - /** Start line of range to search possible breakpoint locations in. If only the line is specified, the request returns all possible locations in that line. */ - line: number; - /** Optional start column of range to search possible breakpoint locations in. If no start column is given, the first column in the start line is assumed. */ - column?: number; - /** Optional end line of range to search possible breakpoint locations in. If no end line is given, then the end line is assumed to be the start line. */ - endLine?: number; - /** Optional end column of range to search possible breakpoint locations in. If no end column is given, then it is assumed to be in the last column of the end line. */ - endColumn?: number; - } - - /** Response to 'breakpointLocations' request. - Contains possible locations for source breakpoints. - */ - export interface BreakpointLocationsResponse extends Response { - body: { - /** Sorted set of possible breakpoint locations. */ - breakpoints: BreakpointLocation[]; - }; - } - - /** SetBreakpoints request; value of command field is 'setBreakpoints'. - Sets multiple breakpoints for a single source and clears all previous breakpoints in that source. - To clear all breakpoint for a source, specify an empty array. - When a breakpoint is hit, a 'stopped' event (with reason 'breakpoint') is generated. - */ - export interface SetBreakpointsRequest extends Request { - // command: 'setBreakpoints'; - arguments: SetBreakpointsArguments; - } - - /** Arguments for 'setBreakpoints' request. */ - export interface SetBreakpointsArguments { - /** The source location of the breakpoints; either 'source.path' or 'source.reference' must be specified. */ - source: Source; - /** The code locations of the breakpoints. */ - breakpoints?: SourceBreakpoint[]; - /** Deprecated: The code locations of the breakpoints. */ - lines?: number[]; - /** A value of true indicates that the underlying source has been modified which results in new breakpoint locations. */ - sourceModified?: boolean; - } - - /** Response to 'setBreakpoints' request. - Returned is information about each breakpoint created by this request. - This includes the actual code location and whether the breakpoint could be verified. - The breakpoints returned are in the same order as the elements of the 'breakpoints' - (or the deprecated 'lines') array in the arguments. - */ - export interface SetBreakpointsResponse extends Response { - body: { - /** Information about the breakpoints. The array elements are in the same order as the elements of the 'breakpoints' (or the deprecated 'lines') array in the arguments. */ - breakpoints: Breakpoint[]; - }; - } - - /** SetFunctionBreakpoints request; value of command field is 'setFunctionBreakpoints'. - Replaces all existing function breakpoints with new function breakpoints. - To clear all function breakpoints, specify an empty array. - When a function breakpoint is hit, a 'stopped' event (with reason 'function breakpoint') is generated. - */ - export interface SetFunctionBreakpointsRequest extends Request { - // command: 'setFunctionBreakpoints'; - arguments: SetFunctionBreakpointsArguments; - } - - /** Arguments for 'setFunctionBreakpoints' request. */ - export interface SetFunctionBreakpointsArguments { - /** The function names of the breakpoints. */ - breakpoints: FunctionBreakpoint[]; - } - - /** Response to 'setFunctionBreakpoints' request. - Returned is information about each breakpoint created by this request. - */ - export interface SetFunctionBreakpointsResponse extends Response { - body: { - /** Information about the breakpoints. The array elements correspond to the elements of the 'breakpoints' array. */ - breakpoints: Breakpoint[]; - }; - } - - /** SetExceptionBreakpoints request; value of command field is 'setExceptionBreakpoints'. - The request configures the debuggers response to thrown exceptions. If an exception is configured to break, a 'stopped' event is fired (with reason 'exception'). - */ - export interface SetExceptionBreakpointsRequest extends Request { - // command: 'setExceptionBreakpoints'; - arguments: SetExceptionBreakpointsArguments; - } - - /** Arguments for 'setExceptionBreakpoints' request. */ - export interface SetExceptionBreakpointsArguments { - /** IDs of checked exception options. The set of IDs is returned via the 'exceptionBreakpointFilters' capability. */ - filters: string[]; - /** Configuration options for selected exceptions. */ - exceptionOptions?: ExceptionOptions[]; - } - - /** Response to 'setExceptionBreakpoints' request. This is just an acknowledgement, so no body field is required. */ - export interface SetExceptionBreakpointsResponse extends Response { - } - - /** DataBreakpointInfo request; value of command field is 'dataBreakpointInfo'. - Obtains information on a possible data breakpoint that could be set on an expression or variable. - */ - export interface DataBreakpointInfoRequest extends Request { - // command: 'dataBreakpointInfo'; - arguments: DataBreakpointInfoArguments; - } - - /** Arguments for 'dataBreakpointInfo' request. */ - export interface DataBreakpointInfoArguments { - /** Reference to the Variable container if the data breakpoint is requested for a child of the container. */ - variablesReference?: number; - /** The name of the Variable's child to obtain data breakpoint information for. If variableReference isn’t provided, this can be an expression. */ - name: string; - } - - /** Response to 'dataBreakpointInfo' request. */ - export interface DataBreakpointInfoResponse extends Response { - body: { - /** An identifier for the data on which a data breakpoint can be registered with the setDataBreakpoints request or null if no data breakpoint is available. */ - dataId: string | null; - /** UI string that describes on what data the breakpoint is set on or why a data breakpoint is not available. */ - description: string; - /** Optional attribute listing the available access types for a potential data breakpoint. A UI frontend could surface this information. */ - accessTypes?: DataBreakpointAccessType[]; - /** Optional attribute indicating that a potential data breakpoint could be persisted across sessions. */ - canPersist?: boolean; - }; - } - - /** SetDataBreakpoints request; value of command field is 'setDataBreakpoints'. - Replaces all existing data breakpoints with new data breakpoints. - To clear all data breakpoints, specify an empty array. - When a data breakpoint is hit, a 'stopped' event (with reason 'data breakpoint') is generated. - */ - export interface SetDataBreakpointsRequest extends Request { - // command: 'setDataBreakpoints'; - arguments: SetDataBreakpointsArguments; - } - - /** Arguments for 'setDataBreakpoints' request. */ - export interface SetDataBreakpointsArguments { - /** The contents of this array replaces all existing data breakpoints. An empty array clears all data breakpoints. */ - breakpoints: DataBreakpoint[]; - } - - /** Response to 'setDataBreakpoints' request. - Returned is information about each breakpoint created by this request. - */ - export interface SetDataBreakpointsResponse extends Response { - body: { - /** Information about the data breakpoints. The array elements correspond to the elements of the input argument 'breakpoints' array. */ - breakpoints: Breakpoint[]; - }; - } - - /** Continue request; value of command field is 'continue'. - The request starts the debuggee to run again. - */ - export interface ContinueRequest extends Request { - // command: 'continue'; - arguments: ContinueArguments; - } - - /** Arguments for 'continue' request. */ - export interface ContinueArguments { - /** Continue execution for the specified thread (if possible). If the backend cannot continue on a single thread but will continue on all threads, it should set the 'allThreadsContinued' attribute in the response to true. */ - threadId: number; - } - - /** Response to 'continue' request. */ - export interface ContinueResponse extends Response { - body: { - /** If true, the 'continue' request has ignored the specified thread and continued all threads instead. If this attribute is missing a value of 'true' is assumed for backward compatibility. */ - allThreadsContinued?: boolean; - }; - } - - /** Next request; value of command field is 'next'. - The request starts the debuggee to run again for one step. - The debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed. - */ - export interface NextRequest extends Request { - // command: 'next'; - arguments: NextArguments; - } - - /** Arguments for 'next' request. */ - export interface NextArguments { - /** Execute 'next' for this thread. */ - threadId: number; - } - - /** Response to 'next' request. This is just an acknowledgement, so no body field is required. */ - export interface NextResponse extends Response { - } - - /** StepIn request; value of command field is 'stepIn'. - The request starts the debuggee to step into a function/method if possible. - If it cannot step into a target, 'stepIn' behaves like 'next'. - The debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed. - If there are multiple function/method calls (or other targets) on the source line, - the optional argument 'targetId' can be used to control into which target the 'stepIn' should occur. - The list of possible targets for a given source line can be retrieved via the 'stepInTargets' request. - */ - export interface StepInRequest extends Request { - // command: 'stepIn'; - arguments: StepInArguments; - } - - /** Arguments for 'stepIn' request. */ - export interface StepInArguments { - /** Execute 'stepIn' for this thread. */ - threadId: number; - /** Optional id of the target to step into. */ - targetId?: number; - } - - /** Response to 'stepIn' request. This is just an acknowledgement, so no body field is required. */ - export interface StepInResponse extends Response { - } - - /** StepOut request; value of command field is 'stepOut'. - The request starts the debuggee to run again for one step. - The debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed. - */ - export interface StepOutRequest extends Request { - // command: 'stepOut'; - arguments: StepOutArguments; - } - - /** Arguments for 'stepOut' request. */ - export interface StepOutArguments { - /** Execute 'stepOut' for this thread. */ - threadId: number; - } - - /** Response to 'stepOut' request. This is just an acknowledgement, so no body field is required. */ - export interface StepOutResponse extends Response { - } - - /** StepBack request; value of command field is 'stepBack'. - The request starts the debuggee to run one step backwards. - The debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed. Clients should only call this request if the capability 'supportsStepBack' is true. - */ - export interface StepBackRequest extends Request { - // command: 'stepBack'; - arguments: StepBackArguments; - } - - /** Arguments for 'stepBack' request. */ - export interface StepBackArguments { - /** Execute 'stepBack' for this thread. */ - threadId: number; - } - - /** Response to 'stepBack' request. This is just an acknowledgement, so no body field is required. */ - export interface StepBackResponse extends Response { - } - - /** ReverseContinue request; value of command field is 'reverseContinue'. - The request starts the debuggee to run backward. Clients should only call this request if the capability 'supportsStepBack' is true. - */ - export interface ReverseContinueRequest extends Request { - // command: 'reverseContinue'; - arguments: ReverseContinueArguments; - } - - /** Arguments for 'reverseContinue' request. */ - export interface ReverseContinueArguments { - /** Execute 'reverseContinue' for this thread. */ - threadId: number; - } - - /** Response to 'reverseContinue' request. This is just an acknowledgement, so no body field is required. */ - export interface ReverseContinueResponse extends Response { - } - - /** RestartFrame request; value of command field is 'restartFrame'. - The request restarts execution of the specified stackframe. - The debug adapter first sends the response and then a 'stopped' event (with reason 'restart') after the restart has completed. - */ - export interface RestartFrameRequest extends Request { - // command: 'restartFrame'; - arguments: RestartFrameArguments; - } - - /** Arguments for 'restartFrame' request. */ - export interface RestartFrameArguments { - /** Restart this stackframe. */ - frameId: number; - } - - /** Response to 'restartFrame' request. This is just an acknowledgement, so no body field is required. */ - export interface RestartFrameResponse extends Response { - } - - /** Goto request; value of command field is 'goto'. - The request sets the location where the debuggee will continue to run. - This makes it possible to skip the execution of code or to executed code again. - The code between the current location and the goto target is not executed but skipped. - The debug adapter first sends the response and then a 'stopped' event with reason 'goto'. - */ - export interface GotoRequest extends Request { - // command: 'goto'; - arguments: GotoArguments; - } - - /** Arguments for 'goto' request. */ - export interface GotoArguments { - /** Set the goto target for this thread. */ - threadId: number; - /** The location where the debuggee will continue to run. */ - targetId: number; - } - - /** Response to 'goto' request. This is just an acknowledgement, so no body field is required. */ - export interface GotoResponse extends Response { - } - - /** Pause request; value of command field is 'pause'. - The request suspends the debuggee. - The debug adapter first sends the response and then a 'stopped' event (with reason 'pause') after the thread has been paused successfully. - */ - export interface PauseRequest extends Request { - // command: 'pause'; - arguments: PauseArguments; - } - - /** Arguments for 'pause' request. */ - export interface PauseArguments { - /** Pause execution for this thread. */ - threadId: number; - } - - /** Response to 'pause' request. This is just an acknowledgement, so no body field is required. */ - export interface PauseResponse extends Response { - } - - /** StackTrace request; value of command field is 'stackTrace'. - The request returns a stacktrace from the current execution state. - */ - export interface StackTraceRequest extends Request { - // command: 'stackTrace'; - arguments: StackTraceArguments; - } - - /** Arguments for 'stackTrace' request. */ - export interface StackTraceArguments { - /** Retrieve the stacktrace for this thread. */ - threadId: number; - /** The index of the first frame to return; if omitted frames start at 0. */ - startFrame?: number; - /** The maximum number of frames to return. If levels is not specified or 0, all frames are returned. */ - levels?: number; - /** Specifies details on how to format the stack frames. */ - format?: StackFrameFormat; - } - - /** Response to 'stackTrace' request. */ - export interface StackTraceResponse extends Response { - body: { - /** The frames of the stackframe. If the array has length zero, there are no stackframes available. - This means that there is no location information available. - */ - stackFrames: StackFrame[]; - /** The total number of frames available. */ - totalFrames?: number; - }; - } - - /** Scopes request; value of command field is 'scopes'. - The request returns the variable scopes for a given stackframe ID. - */ - export interface ScopesRequest extends Request { - // command: 'scopes'; - arguments: ScopesArguments; - } - - /** Arguments for 'scopes' request. */ - export interface ScopesArguments { - /** Retrieve the scopes for this stackframe. */ - frameId: number; - } - - /** Response to 'scopes' request. */ - export interface ScopesResponse extends Response { - body: { - /** The scopes of the stackframe. If the array has length zero, there are no scopes available. */ - scopes: Scope[]; - }; - } - - /** Variables request; value of command field is 'variables'. - Retrieves all child variables for the given variable reference. - An optional filter can be used to limit the fetched children to either named or indexed children. - */ - export interface VariablesRequest extends Request { - // command: 'variables'; - arguments: VariablesArguments; - } - - /** Arguments for 'variables' request. */ - export interface VariablesArguments { - /** The Variable reference. */ - variablesReference: number; - /** Optional filter to limit the child variables to either named or indexed. If omitted, both types are fetched. */ - filter?: 'indexed' | 'named'; - /** The index of the first variable to return; if omitted children start at 0. */ - start?: number; - /** The number of variables to return. If count is missing or 0, all variables are returned. */ - count?: number; - /** Specifies details on how to format the Variable values. */ - format?: ValueFormat; - } - - /** Response to 'variables' request. */ - export interface VariablesResponse extends Response { - body: { - /** All (or a range) of variables for the given variable reference. */ - variables: Variable[]; - }; - } - - /** SetVariable request; value of command field is 'setVariable'. - Set the variable with the given name in the variable container to a new value. - */ - export interface SetVariableRequest extends Request { - // command: 'setVariable'; - arguments: SetVariableArguments; - } - - /** Arguments for 'setVariable' request. */ - export interface SetVariableArguments { - /** The reference of the variable container. */ - variablesReference: number; - /** The name of the variable in the container. */ - name: string; - /** The value of the variable. */ - value: string; - /** Specifies details on how to format the response value. */ - format?: ValueFormat; - } - - /** Response to 'setVariable' request. */ - export interface SetVariableResponse extends Response { - body: { - /** The new value of the variable. */ - value: string; - /** The type of the new value. Typically shown in the UI when hovering over the value. */ - type?: string; - /** If variablesReference is > 0, the new value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. The value should be less than or equal to 2147483647 (2^31 - 1). */ - variablesReference?: number; - /** The number of named child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). - */ - namedVariables?: number; - /** The number of indexed child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). - */ - indexedVariables?: number; - }; - } - - /** Source request; value of command field is 'source'. - The request retrieves the source code for a given source reference. - */ - export interface SourceRequest extends Request { - // command: 'source'; - arguments: SourceArguments; - } - - /** Arguments for 'source' request. */ - export interface SourceArguments { - /** Specifies the source content to load. Either source.path or source.sourceReference must be specified. */ - source?: Source; - /** The reference to the source. This is the same as source.sourceReference. This is provided for backward compatibility since old backends do not understand the 'source' attribute. */ - sourceReference: number; - } - - /** Response to 'source' request. */ - export interface SourceResponse extends Response { - body: { - /** Content of the source reference. */ - content: string; - /** Optional content type (mime type) of the source. */ - mimeType?: string; - }; - } - - /** Threads request; value of command field is 'threads'. - The request retrieves a list of all threads. - */ - export interface ThreadsRequest extends Request { - // command: 'threads'; - } - - /** Response to 'threads' request. */ - export interface ThreadsResponse extends Response { - body: { - /** All threads. */ - threads: Thread[]; - }; - } - - /** TerminateThreads request; value of command field is 'terminateThreads'. - The request terminates the threads with the given ids. - */ - export interface TerminateThreadsRequest extends Request { - // command: 'terminateThreads'; - arguments: TerminateThreadsArguments; - } - - /** Arguments for 'terminateThreads' request. */ - export interface TerminateThreadsArguments { - /** Ids of threads to be terminated. */ - threadIds?: number[]; - } - - /** Response to 'terminateThreads' request. This is just an acknowledgement, so no body field is required. */ - export interface TerminateThreadsResponse extends Response { - } - - /** Modules request; value of command field is 'modules'. - Modules can be retrieved from the debug adapter with the ModulesRequest which can either return all modules or a range of modules to support paging. - */ - export interface ModulesRequest extends Request { - // command: 'modules'; - arguments: ModulesArguments; - } - - /** Arguments for 'modules' request. */ - export interface ModulesArguments { - /** The index of the first module to return; if omitted modules start at 0. */ - startModule?: number; - /** The number of modules to return. If moduleCount is not specified or 0, all modules are returned. */ - moduleCount?: number; - } - - /** Response to 'modules' request. */ - export interface ModulesResponse extends Response { - body: { - /** All modules or range of modules. */ - modules: Module[]; - /** The total number of modules available. */ - totalModules?: number; - }; - } - - /** LoadedSources request; value of command field is 'loadedSources'. - Retrieves the set of all sources currently loaded by the debugged process. - */ - export interface LoadedSourcesRequest extends Request { - // command: 'loadedSources'; - arguments?: LoadedSourcesArguments; - } - - /** Arguments for 'loadedSources' request. */ - export interface LoadedSourcesArguments { - } - - /** Response to 'loadedSources' request. */ - export interface LoadedSourcesResponse extends Response { - body: { - /** Set of loaded sources. */ - sources: Source[]; - }; - } - - /** Evaluate request; value of command field is 'evaluate'. - Evaluates the given expression in the context of the top most stack frame. - The expression has access to any variables and arguments that are in scope. - */ - export interface EvaluateRequest extends Request { - // command: 'evaluate'; - arguments: EvaluateArguments; - } - - /** Arguments for 'evaluate' request. */ - export interface EvaluateArguments { - /** The expression to evaluate. */ - expression: string; - /** Evaluate the expression in the scope of this stack frame. If not specified, the expression is evaluated in the global scope. */ - frameId?: number; - /** The context in which the evaluate request is run. - Values: - 'watch': evaluate is run in a watch. - 'repl': evaluate is run from REPL console. - 'hover': evaluate is run from a data hover. - etc. - */ - context?: string; - /** Specifies details on how to format the Evaluate result. */ - format?: ValueFormat; - } - - /** Response to 'evaluate' request. */ - export interface EvaluateResponse extends Response { - body: { - /** The result of the evaluate request. */ - result: string; - /** The optional type of the evaluate result. */ - type?: string; - /** Properties of a evaluate result that can be used to determine how to render the result in the UI. */ - presentationHint?: VariablePresentationHint; - /** If variablesReference is > 0, the evaluate result is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. The value should be less than or equal to 2147483647 (2^31 - 1). */ - variablesReference: number; - /** The number of named child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). - */ - namedVariables?: number; - /** The number of indexed child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). - */ - indexedVariables?: number; - /** Memory reference to a location appropriate for this result. For pointer type eval results, this is generally a reference to the memory address contained in the pointer. */ - memoryReference?: string; - }; - } - - /** SetExpression request; value of command field is 'setExpression'. - Evaluates the given 'value' expression and assigns it to the 'expression' which must be a modifiable l-value. - The expressions have access to any variables and arguments that are in scope of the specified frame. - */ - export interface SetExpressionRequest extends Request { - // command: 'setExpression'; - arguments: SetExpressionArguments; - } - - /** Arguments for 'setExpression' request. */ - export interface SetExpressionArguments { - /** The l-value expression to assign to. */ - expression: string; - /** The value expression to assign to the l-value expression. */ - value: string; - /** Evaluate the expressions in the scope of this stack frame. If not specified, the expressions are evaluated in the global scope. */ - frameId?: number; - /** Specifies how the resulting value should be formatted. */ - format?: ValueFormat; - } - - /** Response to 'setExpression' request. */ - export interface SetExpressionResponse extends Response { - body: { - /** The new value of the expression. */ - value: string; - /** The optional type of the value. */ - type?: string; - /** Properties of a value that can be used to determine how to render the result in the UI. */ - presentationHint?: VariablePresentationHint; - /** If variablesReference is > 0, the value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. The value should be less than or equal to 2147483647 (2^31 - 1). */ - variablesReference?: number; - /** The number of named child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). - */ - namedVariables?: number; - /** The number of indexed child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). - */ - indexedVariables?: number; - }; - } - - /** StepInTargets request; value of command field is 'stepInTargets'. - This request retrieves the possible stepIn targets for the specified stack frame. - These targets can be used in the 'stepIn' request. - The StepInTargets may only be called if the 'supportsStepInTargetsRequest' capability exists and is true. - */ - export interface StepInTargetsRequest extends Request { - // command: 'stepInTargets'; - arguments: StepInTargetsArguments; - } - - /** Arguments for 'stepInTargets' request. */ - export interface StepInTargetsArguments { - /** The stack frame for which to retrieve the possible stepIn targets. */ - frameId: number; - } - - /** Response to 'stepInTargets' request. */ - export interface StepInTargetsResponse extends Response { - body: { - /** The possible stepIn targets of the specified source location. */ - targets: StepInTarget[]; - }; - } - - /** GotoTargets request; value of command field is 'gotoTargets'. - This request retrieves the possible goto targets for the specified source location. - These targets can be used in the 'goto' request. - The GotoTargets request may only be called if the 'supportsGotoTargetsRequest' capability exists and is true. - */ - export interface GotoTargetsRequest extends Request { - // command: 'gotoTargets'; - arguments: GotoTargetsArguments; - } - - /** Arguments for 'gotoTargets' request. */ - export interface GotoTargetsArguments { - /** The source location for which the goto targets are determined. */ - source: Source; - /** The line location for which the goto targets are determined. */ - line: number; - /** An optional column location for which the goto targets are determined. */ - column?: number; - } - - /** Response to 'gotoTargets' request. */ - export interface GotoTargetsResponse extends Response { - body: { - /** The possible goto targets of the specified location. */ - targets: GotoTarget[]; - }; - } - - /** Completions request; value of command field is 'completions'. - Returns a list of possible completions for a given caret position and text. - The CompletionsRequest may only be called if the 'supportsCompletionsRequest' capability exists and is true. - */ - export interface CompletionsRequest extends Request { - // command: 'completions'; - arguments: CompletionsArguments; - } - - /** Arguments for 'completions' request. */ - export interface CompletionsArguments { - /** Returns completions in the scope of this stack frame. If not specified, the completions are returned for the global scope. */ - frameId?: number; - /** One or more source lines. Typically this is the text a user has typed into the debug console before he asked for completion. */ - text: string; - /** The character position for which to determine the completion proposals. */ - column: number; - /** An optional line for which to determine the completion proposals. If missing the first line of the text is assumed. */ - line?: number; - } - - /** Response to 'completions' request. */ - export interface CompletionsResponse extends Response { - body: { - /** The possible completions for . */ - targets: CompletionItem[]; - }; - } - - /** ExceptionInfo request; value of command field is 'exceptionInfo'. - Retrieves the details of the exception that caused this event to be raised. - */ - export interface ExceptionInfoRequest extends Request { - // command: 'exceptionInfo'; - arguments: ExceptionInfoArguments; - } - - /** Arguments for 'exceptionInfo' request. */ - export interface ExceptionInfoArguments { - /** Thread for which exception information should be retrieved. */ - threadId: number; - } - - /** Response to 'exceptionInfo' request. */ - export interface ExceptionInfoResponse extends Response { - body: { - /** ID of the exception that was thrown. */ - exceptionId: string; - /** Descriptive text for the exception provided by the debug adapter. */ - description?: string; - /** Mode that caused the exception notification to be raised. */ - breakMode: ExceptionBreakMode; - /** Detailed information about the exception. */ - details?: ExceptionDetails; - }; - } - - /** ReadMemory request; value of command field is 'readMemory'. - Reads bytes from memory at the provided location. - */ - export interface ReadMemoryRequest extends Request { - // command: 'readMemory'; - arguments: ReadMemoryArguments; - } - - /** Arguments for 'readMemory' request. */ - export interface ReadMemoryArguments { - /** Memory reference to the base location from which data should be read. */ - memoryReference: string; - /** Optional offset (in bytes) to be applied to the reference location before reading data. Can be negative. */ - offset?: number; - /** Number of bytes to read at the specified location and offset. */ - count: number; - } - - /** Response to 'readMemory' request. */ - export interface ReadMemoryResponse extends Response { - body?: { - /** The address of the first byte of data returned. Treated as a hex value if prefixed with '0x', or as a decimal value otherwise. */ - address: string; - /** The number of unreadable bytes encountered after the last successfully read byte. This can be used to determine the number of bytes that must be skipped before a subsequent 'readMemory' request will succeed. */ - unreadableBytes?: number; - /** The bytes read from memory, encoded using base64. */ - data?: string; - }; - } - - /** Disassemble request; value of command field is 'disassemble'. - Disassembles code stored at the provided location. - */ - export interface DisassembleRequest extends Request { - // command: 'disassemble'; - arguments: DisassembleArguments; - } - - /** Arguments for 'disassemble' request. */ - export interface DisassembleArguments { - /** Memory reference to the base location containing the instructions to disassemble. */ - memoryReference: string; - /** Optional offset (in bytes) to be applied to the reference location before disassembling. Can be negative. */ - offset?: number; - /** Optional offset (in instructions) to be applied after the byte offset (if any) before disassembling. Can be negative. */ - instructionOffset?: number; - /** Number of instructions to disassemble starting at the specified location and offset. An adapter must return exactly this number of instructions - any unavailable instructions should be replaced with an implementation-defined 'invalid instruction' value. */ - instructionCount: number; - /** If true, the adapter should attempt to resolve memory addresses and other values to symbolic names. */ - resolveSymbols?: boolean; - } - - /** Response to 'disassemble' request. */ - export interface DisassembleResponse extends Response { - body?: { - /** The list of disassembled instructions. */ - instructions: DisassembledInstruction[]; - }; - } - - /** Information about the capabilities of a debug adapter. */ - export interface Capabilities { - /** The debug adapter supports the 'configurationDone' request. */ - supportsConfigurationDoneRequest?: boolean; - /** The debug adapter supports function breakpoints. */ - supportsFunctionBreakpoints?: boolean; - /** The debug adapter supports conditional breakpoints. */ - supportsConditionalBreakpoints?: boolean; - /** The debug adapter supports breakpoints that break execution after a specified number of hits. */ - supportsHitConditionalBreakpoints?: boolean; - /** The debug adapter supports a (side effect free) evaluate request for data hovers. */ - supportsEvaluateForHovers?: boolean; - /** Available filters or options for the setExceptionBreakpoints request. */ - exceptionBreakpointFilters?: ExceptionBreakpointsFilter[]; - /** The debug adapter supports stepping back via the 'stepBack' and 'reverseContinue' requests. */ - supportsStepBack?: boolean; - /** The debug adapter supports setting a variable to a value. */ - supportsSetVariable?: boolean; - /** The debug adapter supports restarting a frame. */ - supportsRestartFrame?: boolean; - /** The debug adapter supports the 'gotoTargets' request. */ - supportsGotoTargetsRequest?: boolean; - /** The debug adapter supports the 'stepInTargets' request. */ - supportsStepInTargetsRequest?: boolean; - /** The debug adapter supports the 'completions' request. */ - supportsCompletionsRequest?: boolean; - /** The set of characters that should trigger completion in a REPL. If not specified, the UI should assume the '.' character. */ - completionTriggerCharacters?: string[]; - /** The debug adapter supports the 'modules' request. */ - supportsModulesRequest?: boolean; - /** The set of additional module information exposed by the debug adapter. */ - additionalModuleColumns?: ColumnDescriptor[]; - /** Checksum algorithms supported by the debug adapter. */ - supportedChecksumAlgorithms?: ChecksumAlgorithm[]; - /** The debug adapter supports the 'restart' request. In this case a client should not implement 'restart' by terminating and relaunching the adapter but by calling the RestartRequest. */ - supportsRestartRequest?: boolean; - /** The debug adapter supports 'exceptionOptions' on the setExceptionBreakpoints request. */ - supportsExceptionOptions?: boolean; - /** The debug adapter supports a 'format' attribute on the stackTraceRequest, variablesRequest, and evaluateRequest. */ - supportsValueFormattingOptions?: boolean; - /** The debug adapter supports the 'exceptionInfo' request. */ - supportsExceptionInfoRequest?: boolean; - /** The debug adapter supports the 'terminateDebuggee' attribute on the 'disconnect' request. */ - supportTerminateDebuggee?: boolean; - /** The debug adapter supports the delayed loading of parts of the stack, which requires that both the 'startFrame' and 'levels' arguments and the 'totalFrames' result of the 'StackTrace' request are supported. */ - supportsDelayedStackTraceLoading?: boolean; - /** The debug adapter supports the 'loadedSources' request. */ - supportsLoadedSourcesRequest?: boolean; - /** The debug adapter supports logpoints by interpreting the 'logMessage' attribute of the SourceBreakpoint. */ - supportsLogPoints?: boolean; - /** The debug adapter supports the 'terminateThreads' request. */ - supportsTerminateThreadsRequest?: boolean; - /** The debug adapter supports the 'setExpression' request. */ - supportsSetExpression?: boolean; - /** The debug adapter supports the 'terminate' request. */ - supportsTerminateRequest?: boolean; - /** The debug adapter supports data breakpoints. */ - supportsDataBreakpoints?: boolean; - /** The debug adapter supports the 'readMemory' request. */ - supportsReadMemoryRequest?: boolean; - /** The debug adapter supports the 'disassemble' request. */ - supportsDisassembleRequest?: boolean; - /** The debug adapter supports the 'cancel' request. */ - supportsCancelRequest?: boolean; - /** The debug adapter supports the 'breakpointLocations' request. */ - supportsBreakpointLocationsRequest?: boolean; - } - - /** An ExceptionBreakpointsFilter is shown in the UI as an option for configuring how exceptions are dealt with. */ - export interface ExceptionBreakpointsFilter { - /** The internal ID of the filter. This value is passed to the setExceptionBreakpoints request. */ - filter: string; - /** The name of the filter. This will be shown in the UI. */ - label: string; - /** Initial value of the filter. If not specified a value 'false' is assumed. */ - default?: boolean; - } - - /** A structured message object. Used to return errors from requests. */ - export interface Message { - /** Unique identifier for the message. */ - id: number; - /** A format string for the message. Embedded variables have the form '{name}'. - If variable name starts with an underscore character, the variable does not contain user data (PII) and can be safely used for telemetry purposes. - */ - format: string; - /** An object used as a dictionary for looking up the variables in the format string. */ - variables?: { [key: string]: string; }; - /** If true send to telemetry. */ - sendTelemetry?: boolean; - /** If true show user. */ - showUser?: boolean; - /** An optional url where additional information about this message can be found. */ - url?: string; - /** An optional label that is presented to the user as the UI for opening the url. */ - urlLabel?: string; - } - - /** A Module object represents a row in the modules view. - Two attributes are mandatory: an id identifies a module in the modules view and is used in a ModuleEvent for identifying a module for adding, updating or deleting. - The name is used to minimally render the module in the UI. - - Additional attributes can be added to the module. They will show up in the module View if they have a corresponding ColumnDescriptor. - - To avoid an unnecessary proliferation of additional attributes with similar semantics but different names - we recommend to re-use attributes from the 'recommended' list below first, and only introduce new attributes if nothing appropriate could be found. - */ - export interface Module { - /** Unique identifier for the module. */ - id: number | string; - /** A name of the module. */ - name: string; - /** optional but recommended attributes. - always try to use these first before introducing additional attributes. - - Logical full path to the module. The exact definition is implementation defined, but usually this would be a full path to the on-disk file for the module. - */ - path?: string; - /** True if the module is optimized. */ - isOptimized?: boolean; - /** True if the module is considered 'user code' by a debugger that supports 'Just My Code'. */ - isUserCode?: boolean; - /** Version of Module. */ - version?: string; - /** User understandable description of if symbols were found for the module (ex: 'Symbols Loaded', 'Symbols not found', etc. */ - symbolStatus?: string; - /** Logical full path to the symbol file. The exact definition is implementation defined. */ - symbolFilePath?: string; - /** Module created or modified. */ - dateTimeStamp?: string; - /** Address range covered by this module. */ - addressRange?: string; - } - - /** A ColumnDescriptor specifies what module attribute to show in a column of the ModulesView, how to format it, and what the column's label should be. - It is only used if the underlying UI actually supports this level of customization. - */ - export interface ColumnDescriptor { - /** Name of the attribute rendered in this column. */ - attributeName: string; - /** Header UI label of column. */ - label: string; - /** Format to use for the rendered values in this column. TBD how the format strings looks like. */ - format?: string; - /** Datatype of values in this column. Defaults to 'string' if not specified. */ - type?: 'string' | 'number' | 'boolean' | 'unixTimestampUTC'; - /** Width of this column in characters (hint only). */ - width?: number; - } - - /** The ModulesViewDescriptor is the container for all declarative configuration options of a ModuleView. - For now it only specifies the columns to be shown in the modules view. - */ - export interface ModulesViewDescriptor { - columns: ColumnDescriptor[]; - } - - /** A Thread */ - export interface Thread { - /** Unique identifier for the thread. */ - id: number; - /** A name of the thread. */ - name: string; - } - - /** A Source is a descriptor for source code. It is returned from the debug adapter as part of a StackFrame and it is used by clients when specifying breakpoints. */ - export interface Source { - /** The short name of the source. Every source returned from the debug adapter has a name. When sending a source to the debug adapter this name is optional. */ - name?: string; - /** The path of the source to be shown in the UI. It is only used to locate and load the content of the source if no sourceReference is specified (or its value is 0). */ - path?: string; - /** If sourceReference > 0 the contents of the source must be retrieved through the SourceRequest (even if a path is specified). A sourceReference is only valid for a session, so it must not be used to persist a source. The value should be less than or equal to 2147483647 (2^31 - 1). */ - sourceReference?: number; - /** An optional hint for how to present the source in the UI. A value of 'deemphasize' can be used to indicate that the source is not available or that it is skipped on stepping. */ - presentationHint?: 'normal' | 'emphasize' | 'deemphasize'; - /** The (optional) origin of this source: possible values 'internal module', 'inlined content from source map', etc. */ - origin?: string; - /** An optional list of sources that are related to this source. These may be the source that generated this source. */ - sources?: Source[]; - /** Optional data that a debug adapter might want to loop through the client. The client should leave the data intact and persist it across sessions. The client should not interpret the data. */ - adapterData?: any; - /** The checksums associated with this file. */ - checksums?: Checksum[]; - } - - /** A Stackframe contains the source location. */ - export interface StackFrame { - /** An identifier for the stack frame. It must be unique across all threads. This id can be used to retrieve the scopes of the frame with the 'scopesRequest' or to restart the execution of a stackframe. */ - id: number; - /** The name of the stack frame, typically a method name. */ - name: string; - /** The optional source of the frame. */ - source?: Source; - /** The line within the file of the frame. If source is null or doesn't exist, line is 0 and must be ignored. */ - line: number; - /** The column within the line. If source is null or doesn't exist, column is 0 and must be ignored. */ - column: number; - /** An optional end line of the range covered by the stack frame. */ - endLine?: number; - /** An optional end column of the range covered by the stack frame. */ - endColumn?: number; - /** Optional memory reference for the current instruction pointer in this frame. */ - instructionPointerReference?: string; - /** The module associated with this frame, if any. */ - moduleId?: number | string; - /** An optional hint for how to present this frame in the UI. A value of 'label' can be used to indicate that the frame is an artificial frame that is used as a visual label or separator. A value of 'subtle' can be used to change the appearance of a frame in a 'subtle' way. */ - presentationHint?: 'normal' | 'label' | 'subtle'; - } - - /** A Scope is a named container for variables. Optionally a scope can map to a source or a range within a source. */ - export interface Scope { - /** Name of the scope such as 'Arguments', 'Locals', or 'Registers'. This string is shown in the UI as is and can be translated. */ - name: string; - /** An optional hint for how to present this scope in the UI. If this attribute is missing, the scope is shown with a generic UI. - Values: - 'arguments': Scope contains method arguments. - 'locals': Scope contains local variables. - 'registers': Scope contains registers. Only a single 'registers' scope should be returned from a 'scopes' request. - etc. - */ - presentationHint?: string; - /** The variables of this scope can be retrieved by passing the value of variablesReference to the VariablesRequest. */ - variablesReference: number; - /** The number of named variables in this scope. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. - */ - namedVariables?: number; - /** The number of indexed variables in this scope. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. - */ - indexedVariables?: number; - /** If true, the number of variables in this scope is large or expensive to retrieve. */ - expensive: boolean; - /** Optional source for this scope. */ - source?: Source; - /** Optional start line of the range covered by this scope. */ - line?: number; - /** Optional start column of the range covered by this scope. */ - column?: number; - /** Optional end line of the range covered by this scope. */ - endLine?: number; - /** Optional end column of the range covered by this scope. */ - endColumn?: number; - } - - /** A Variable is a name/value pair. - Optionally a variable can have a 'type' that is shown if space permits or when hovering over the variable's name. - An optional 'kind' is used to render additional properties of the variable, e.g. different icons can be used to indicate that a variable is public or private. - If the value is structured (has children), a handle is provided to retrieve the children with the VariablesRequest. - If the number of named or indexed children is large, the numbers should be returned via the optional 'namedVariables' and 'indexedVariables' attributes. - The client can use this optional information to present the children in a paged UI and fetch them in chunks. - */ - export interface Variable { - /** The variable's name. */ - name: string; - /** The variable's value. This can be a multi-line text, e.g. for a function the body of a function. */ - value: string; - /** The type of the variable's value. Typically shown in the UI when hovering over the value. */ - type?: string; - /** Properties of a variable that can be used to determine how to render the variable in the UI. */ - presentationHint?: VariablePresentationHint; - /** Optional evaluatable name of this variable which can be passed to the 'EvaluateRequest' to fetch the variable's value. */ - evaluateName?: string; - /** If variablesReference is > 0, the variable is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. */ - variablesReference: number; - /** The number of named child variables. - The client can use this optional information to present the children in a paged UI and fetch them in chunks. - */ - namedVariables?: number; - /** The number of indexed child variables. - The client can use this optional information to present the children in a paged UI and fetch them in chunks. - */ - indexedVariables?: number; - /** Optional memory reference for the variable if the variable represents executable code, such as a function pointer. */ - memoryReference?: string; - } - - /** Optional properties of a variable that can be used to determine how to render the variable in the UI. */ - export interface VariablePresentationHint { - /** The kind of variable. Before introducing additional values, try to use the listed values. - Values: - 'property': Indicates that the object is a property. - 'method': Indicates that the object is a method. - 'class': Indicates that the object is a class. - 'data': Indicates that the object is data. - 'event': Indicates that the object is an event. - 'baseClass': Indicates that the object is a base class. - 'innerClass': Indicates that the object is an inner class. - 'interface': Indicates that the object is an interface. - 'mostDerivedClass': Indicates that the object is the most derived class. - 'virtual': Indicates that the object is virtual, that means it is a synthetic object introduced by the adapter for rendering purposes, e.g. an index range for large arrays. - 'dataBreakpoint': Indicates that a data breakpoint is registered for the object. - etc. - */ - kind?: string; - /** Set of attributes represented as an array of strings. Before introducing additional values, try to use the listed values. - Values: - 'static': Indicates that the object is static. - 'constant': Indicates that the object is a constant. - 'readOnly': Indicates that the object is read only. - 'rawString': Indicates that the object is a raw string. - 'hasObjectId': Indicates that the object can have an Object ID created for it. - 'canHaveObjectId': Indicates that the object has an Object ID associated with it. - 'hasSideEffects': Indicates that the evaluation had side effects. - etc. - */ - attributes?: string[]; - /** Visibility of variable. Before introducing additional values, try to use the listed values. - Values: 'public', 'private', 'protected', 'internal', 'final', etc. - */ - visibility?: string; - } - - /** Properties of a breakpoint location returned from the 'breakpointLocations' request. */ - export interface BreakpointLocation { - /** Start line of breakpoint location. */ - line: number; - /** Optional start column of breakpoint location. */ - column?: number; - /** Optional end line of breakpoint location if the location covers a range. */ - endLine?: number; - /** Optional end column of breakpoint location if the location covers a range. */ - endColumn?: number; - } - - /** Properties of a breakpoint or logpoint passed to the setBreakpoints request. */ - export interface SourceBreakpoint { - /** The source line of the breakpoint or logpoint. */ - line: number; - /** An optional source column of the breakpoint. */ - column?: number; - /** An optional expression for conditional breakpoints. */ - condition?: string; - /** An optional expression that controls how many hits of the breakpoint are ignored. The backend is expected to interpret the expression as needed. */ - hitCondition?: string; - /** If this attribute exists and is non-empty, the backend must not 'break' (stop) but log the message instead. Expressions within {} are interpolated. */ - logMessage?: string; - } - - /** Properties of a breakpoint passed to the setFunctionBreakpoints request. */ - export interface FunctionBreakpoint { - /** The name of the function. */ - name: string; - /** An optional expression for conditional breakpoints. */ - condition?: string; - /** An optional expression that controls how many hits of the breakpoint are ignored. The backend is expected to interpret the expression as needed. */ - hitCondition?: string; - } - - /** This enumeration defines all possible access types for data breakpoints. */ - export type DataBreakpointAccessType = 'read' | 'write' | 'readWrite'; - - /** Properties of a data breakpoint passed to the setDataBreakpoints request. */ - export interface DataBreakpoint { - /** An id representing the data. This id is returned from the dataBreakpointInfo request. */ - dataId: string; - /** The access type of the data. */ - accessType?: DataBreakpointAccessType; - /** An optional expression for conditional breakpoints. */ - condition?: string; - /** An optional expression that controls how many hits of the breakpoint are ignored. The backend is expected to interpret the expression as needed. */ - hitCondition?: string; - } - - /** Information about a Breakpoint created in setBreakpoints or setFunctionBreakpoints. */ - export interface Breakpoint { - /** An optional identifier for the breakpoint. It is needed if breakpoint events are used to update or remove breakpoints. */ - id?: number; - /** If true breakpoint could be set (but not necessarily at the desired location). */ - verified: boolean; - /** An optional message about the state of the breakpoint. This is shown to the user and can be used to explain why a breakpoint could not be verified. */ - message?: string; - /** The source where the breakpoint is located. */ - source?: Source; - /** The start line of the actual range covered by the breakpoint. */ - line?: number; - /** An optional start column of the actual range covered by the breakpoint. */ - column?: number; - /** An optional end line of the actual range covered by the breakpoint. */ - endLine?: number; - /** An optional end column of the actual range covered by the breakpoint. If no end line is given, then the end column is assumed to be in the start line. */ - endColumn?: number; - } - - /** A StepInTarget can be used in the 'stepIn' request and determines into which single target the stepIn request should step. */ - export interface StepInTarget { - /** Unique identifier for a stepIn target. */ - id: number; - /** The name of the stepIn target (shown in the UI). */ - label: string; - } - - /** A GotoTarget describes a code location that can be used as a target in the 'goto' request. - The possible goto targets can be determined via the 'gotoTargets' request. - */ - export interface GotoTarget { - /** Unique identifier for a goto target. This is used in the goto request. */ - id: number; - /** The name of the goto target (shown in the UI). */ - label: string; - /** The line of the goto target. */ - line: number; - /** An optional column of the goto target. */ - column?: number; - /** An optional end line of the range covered by the goto target. */ - endLine?: number; - /** An optional end column of the range covered by the goto target. */ - endColumn?: number; - /** Optional memory reference for the instruction pointer value represented by this target. */ - instructionPointerReference?: string; - } - - /** CompletionItems are the suggestions returned from the CompletionsRequest. */ - export interface CompletionItem { - /** The label of this completion item. By default this is also the text that is inserted when selecting this completion. */ - label: string; - /** If text is not falsy then it is inserted instead of the label. */ - text?: string; - /** A string that should be used when comparing this item with other items. When `falsy` the label is used. */ - sortText?: string; - /** The item's type. Typically the client uses this information to render the item in the UI with an icon. */ - type?: CompletionItemType; - /** This value determines the location (in the CompletionsRequest's 'text' attribute) where the completion text is added. - If missing the text is added at the location specified by the CompletionsRequest's 'column' attribute. - */ - start?: number; - /** This value determines how many characters are overwritten by the completion text. - If missing the value 0 is assumed which results in the completion text being inserted. - */ - length?: number; - } - - /** Some predefined types for the CompletionItem. Please note that not all clients have specific icons for all of them. */ - export type CompletionItemType = 'method' | 'function' | 'constructor' | 'field' | 'variable' | 'class' | 'interface' | 'module' | 'property' | 'unit' | 'value' | 'enum' | 'keyword' | 'snippet' | 'text' | 'color' | 'file' | 'reference' | 'customcolor'; - - /** Names of checksum algorithms that may be supported by a debug adapter. */ - export type ChecksumAlgorithm = 'MD5' | 'SHA1' | 'SHA256' | 'timestamp'; - - /** The checksum of an item calculated by the specified algorithm. */ - export interface Checksum { - /** The algorithm used to calculate this checksum. */ - algorithm: ChecksumAlgorithm; - /** Value of the checksum. */ - checksum: string; - } - - /** Provides formatting information for a value. */ - export interface ValueFormat { - /** Display the value in hex. */ - hex?: boolean; - } - - /** Provides formatting information for a stack frame. */ - export interface StackFrameFormat extends ValueFormat { - /** Displays parameters for the stack frame. */ - parameters?: boolean; - /** Displays the types of parameters for the stack frame. */ - parameterTypes?: boolean; - /** Displays the names of parameters for the stack frame. */ - parameterNames?: boolean; - /** Displays the values of parameters for the stack frame. */ - parameterValues?: boolean; - /** Displays the line number of the stack frame. */ - line?: boolean; - /** Displays the module of the stack frame. */ - module?: boolean; - /** Includes all stack frames, including those the debug adapter might otherwise hide. */ - includeAll?: boolean; - } - - /** An ExceptionOptions assigns configuration options to a set of exceptions. */ - export interface ExceptionOptions { - /** A path that selects a single or multiple exceptions in a tree. If 'path' is missing, the whole tree is selected. By convention the first segment of the path is a category that is used to group exceptions in the UI. */ - path?: ExceptionPathSegment[]; - /** Condition when a thrown exception should result in a break. */ - breakMode: ExceptionBreakMode; - } - - /** This enumeration defines all possible conditions when a thrown exception should result in a break. - never: never breaks, - always: always breaks, - unhandled: breaks when exception unhandled, - userUnhandled: breaks if the exception is not handled by user code. - */ - export type ExceptionBreakMode = 'never' | 'always' | 'unhandled' | 'userUnhandled'; - - /** An ExceptionPathSegment represents a segment in a path that is used to match leafs or nodes in a tree of exceptions. If a segment consists of more than one name, it matches the names provided if 'negate' is false or missing or it matches anything except the names provided if 'negate' is true. */ - export interface ExceptionPathSegment { - /** If false or missing this segment matches the names provided, otherwise it matches anything except the names provided. */ - negate?: boolean; - /** Depending on the value of 'negate' the names that should match or not match. */ - names: string[]; - } - - /** Detailed information about an exception that has occurred. */ - export interface ExceptionDetails { - /** Message contained in the exception. */ - message?: string; - /** Short type name of the exception object. */ - typeName?: string; - /** Fully-qualified type name of the exception object. */ - fullTypeName?: string; - /** Optional expression that can be evaluated in the current scope to obtain the exception object. */ - evaluateName?: string; - /** Stack trace at the time the exception was thrown. */ - stackTrace?: string; - /** Details of the exception contained by this exception, if any. */ - innerException?: ExceptionDetails[]; - } - - /** Represents a single disassembled instruction. */ - export interface DisassembledInstruction { - /** The address of the instruction. Treated as a hex value if prefixed with '0x', or as a decimal value otherwise. */ - address: string; - /** Optional raw bytes representing the instruction and its operands, in an implementation-defined format. */ - instructionBytes?: string; - /** Text representing the instruction and its operands, in an implementation-defined format. */ - instruction: string; - /** Name of the symbol that corresponds with the location of this instruction, if any. */ - symbol?: string; - /** Source location that corresponds to this instruction, if any. Should always be set (if available) on the first instruction returned, but can be omitted afterwards if this instruction maps to the same source file as the previous instruction. */ - location?: Source; - /** The line within the source location that corresponds to this instruction, if any. */ - line?: number; - /** The column within the line that corresponds to this instruction, if any. */ - column?: number; - /** The end line of the range that corresponds to this instruction, if any. */ - endLine?: number; - /** The end column of the range that corresponds to this instruction, if any. */ - endColumn?: number; - } -} - -//------------------------------------------------------------------------------------------------------------------------------ - -export class Message implements DebugProtocol.ProtocolMessage { - seq: number; - type: string; - - public constructor(type: string) { - this.seq = 0; - this.type = type; - } -} - -export class Response extends Message implements DebugProtocol.Response { - request_seq: number; - success: boolean; - command: string; - - public constructor(request: DebugProtocol.Request, message?: string) { - super('response'); - this.request_seq = request.seq; - this.command = request.command; - if (message) { - this.success = false; - (this).message = message; - } else { - this.success = true; - } - } -} - -export class Event extends Message implements DebugProtocol.Event { - event: string; - - public constructor(event: string, body?: any) { - super('event'); - this.event = event; - if (body) { - (this).body = body; - } - } -} - -//-------------------------------------------------------------------------------------------------------------------------------- - -export class ProtocolServer implements vscode.DebugAdapter { - - private close = new vscode.EventEmitter(); - onClose: vscode.Event = this.close.event; - - private error = new vscode.EventEmitter(); - onError: vscode.Event = this.error.event; - - private sendMessage = new vscode.EventEmitter(); - readonly onDidSendMessage: vscode.Event = this.sendMessage.event; - - private _sequence: number = 1; - private _pendingRequests = new Map void>(); - - - public handleMessage(message: DebugProtocol.ProtocolMessage): void { - this.dispatch(message); - } - - public dispose() { - } - - public sendEvent(event: DebugProtocol.Event): void { - this._send('event', event); - } - - public sendResponse(response: DebugProtocol.Response): void { - if (response.seq > 0) { - console.error(`attempt to send more than one response for command ${response.command}`); - } else { - this._send('response', response); - } - } - - public sendRequest(command: string, args: any, timeout: number, cb: (response: DebugProtocol.Response) => void): void { - - const request: any = { - command: command - }; - if (args && Object.keys(args).length > 0) { - request.arguments = args; - } - - this._send('request', request); - - if (cb) { - this._pendingRequests.set(request.seq, cb); - - const timer = setTimeout(() => { - clearTimeout(timer); - const clb = this._pendingRequests.get(request.seq); - if (clb) { - this._pendingRequests.delete(request.seq); - clb(new Response(request, 'timeout')); - } - }, timeout); - } - } - - // ---- protected ---------------------------------------------------------- - - protected dispatchRequest(_request: DebugProtocol.Request): void { - } - - // ---- private ------------------------------------------------------------ - - private dispatch(msg: DebugProtocol.ProtocolMessage) { - if (msg.type === 'request') { - this.dispatchRequest(msg); - } else if (msg.type === 'response') { - const response = msg; - const clb = this._pendingRequests.get(response.request_seq); - if (clb) { - this._pendingRequests.delete(response.request_seq); - clb(response); - } - } - } - - private _send(typ: 'request' | 'response' | 'event', message: DebugProtocol.ProtocolMessage): void { - - message.type = typ; - message.seq = this._sequence++; - - this.sendMessage.fire(message); - } -} - -//------------------------------------------------------------------------------------------------------------------------------- - -export class Source implements DebugProtocol.Source { - name: string; - path?: string; - sourceReference: number; - - public constructor(name: string, path?: string, id: number = 0, origin?: string, data?: any) { - this.name = name; - this.path = path; - this.sourceReference = id; - if (origin) { - (this).origin = origin; - } - if (data) { - (this).adapterData = data; - } - } -} - -export class Scope implements DebugProtocol.Scope { - name: string; - variablesReference: number; - expensive: boolean; - - public constructor(name: string, reference: number, expensive: boolean = false) { - this.name = name; - this.variablesReference = reference; - this.expensive = expensive; - } -} - -export class StackFrame implements DebugProtocol.StackFrame { - id: number; - source?: Source; - line: number; - column: number; - name: string; - - public constructor(i: number, nm: string, src?: Source, ln: number = 0, col: number = 0) { - this.id = i; - this.source = src; - this.line = ln; - this.column = col; - this.name = nm; - } -} - -export class Thread implements DebugProtocol.Thread { - id: number; - name: string; - - public constructor(id: number, name: string) { - this.id = id; - if (name) { - this.name = name; - } else { - this.name = 'Thread #' + id; - } - } -} - -export class Variable implements DebugProtocol.Variable { - name: string; - value: string; - variablesReference: number; - - public constructor(name: string, value: string, ref: number = 0, indexedVariables?: number, namedVariables?: number) { - this.name = name; - this.value = value; - this.variablesReference = ref; - if (typeof namedVariables === 'number') { - (this).namedVariables = namedVariables; - } - if (typeof indexedVariables === 'number') { - (this).indexedVariables = indexedVariables; - } - } -} - -export class Breakpoint implements DebugProtocol.Breakpoint { - verified: boolean; - - public constructor(verified: boolean, line?: number, column?: number, source?: Source) { - this.verified = verified; - const e: DebugProtocol.Breakpoint = this; - if (typeof line === 'number') { - e.line = line; - } - if (typeof column === 'number') { - e.column = column; - } - if (source) { - e.source = source; - } - } -} - -export class Module implements DebugProtocol.Module { - id: number | string; - name: string; - - public constructor(id: number | string, name: string) { - this.id = id; - this.name = name; - } -} - -export class CompletionItem implements DebugProtocol.CompletionItem { - label: string; - start: number; - length: number; - - public constructor(label: string, start: number, length: number = 0) { - this.label = label; - this.start = start; - this.length = length; - } -} - -export class StoppedEvent extends Event implements DebugProtocol.StoppedEvent { - body: { - reason: string; - }; - - public constructor(reason: string, threadId?: number, exceptionText?: string) { - super('stopped'); - this.body = { - reason: reason - }; - if (typeof threadId === 'number') { - (this as DebugProtocol.StoppedEvent).body.threadId = threadId; - } - if (typeof exceptionText === 'string') { - (this as DebugProtocol.StoppedEvent).body.text = exceptionText; - } - } -} - -export class ContinuedEvent extends Event implements DebugProtocol.ContinuedEvent { - body: { - threadId: number; - }; - - public constructor(threadId: number, allThreadsContinued?: boolean) { - super('continued'); - this.body = { - threadId: threadId - }; - - if (typeof allThreadsContinued === 'boolean') { - (this).body.allThreadsContinued = allThreadsContinued; - } - } -} - -export class InitializedEvent extends Event implements DebugProtocol.InitializedEvent { - public constructor() { - super('initialized'); - } -} - -export class TerminatedEvent extends Event implements DebugProtocol.TerminatedEvent { - public constructor(restart?: any) { - super('terminated'); - if (typeof restart === 'boolean' || restart) { - const e: DebugProtocol.TerminatedEvent = this; - e.body = { - restart: restart - }; - } - } -} - -export class OutputEvent extends Event implements DebugProtocol.OutputEvent { - body: { - category: string, - output: string, - data?: any - }; - - public constructor(output: string, category: string = 'console', data?: any) { - super('output'); - this.body = { - category: category, - output: output - }; - if (data !== undefined) { - this.body.data = data; - } - } -} - -export class ThreadEvent extends Event implements DebugProtocol.ThreadEvent { - body: { - reason: string, - threadId: number - }; - - public constructor(reason: string, threadId: number) { - super('thread'); - this.body = { - reason: reason, - threadId: threadId - }; - } -} - -export class BreakpointEvent extends Event implements DebugProtocol.BreakpointEvent { - body: { - reason: string, - breakpoint: Breakpoint - }; - - public constructor(reason: string, breakpoint: Breakpoint) { - super('breakpoint'); - this.body = { - reason: reason, - breakpoint: breakpoint - }; - } -} - -export class ModuleEvent extends Event implements DebugProtocol.ModuleEvent { - body: { - reason: 'new' | 'changed' | 'removed', - module: Module - }; - - public constructor(reason: 'new' | 'changed' | 'removed', module: Module) { - super('module'); - this.body = { - reason: reason, - module: module - }; - } -} - -export class LoadedSourceEvent extends Event implements DebugProtocol.LoadedSourceEvent { - body: { - reason: 'new' | 'changed' | 'removed', - source: Source - }; - - public constructor(reason: 'new' | 'changed' | 'removed', source: Source) { - super('loadedSource'); - this.body = { - reason: reason, - source: source - }; - } -} - -export class CapabilitiesEvent extends Event implements DebugProtocol.CapabilitiesEvent { - body: { - capabilities: DebugProtocol.Capabilities - }; - - public constructor(capabilities: DebugProtocol.Capabilities) { - super('capabilities'); - this.body = { - capabilities: capabilities - }; - } -} - -export enum ErrorDestination { - User = 1, - Telemetry = 2 -} - -export class DebugSession extends ProtocolServer { - - private _debuggerLinesStartAt1: boolean; - private _debuggerColumnsStartAt1: boolean; - private _debuggerPathsAreURIs: boolean; - - private _clientLinesStartAt1: boolean; - private _clientColumnsStartAt1: boolean; - private _clientPathsAreURIs: boolean; - - protected _isServer: boolean; - - public constructor(obsolete_debuggerLinesAndColumnsStartAt1?: boolean, obsolete_isServer?: boolean) { - super(); - - const linesAndColumnsStartAt1 = typeof obsolete_debuggerLinesAndColumnsStartAt1 === 'boolean' ? obsolete_debuggerLinesAndColumnsStartAt1 : false; - this._debuggerLinesStartAt1 = linesAndColumnsStartAt1; - this._debuggerColumnsStartAt1 = linesAndColumnsStartAt1; - this._debuggerPathsAreURIs = false; - - this._clientLinesStartAt1 = true; - this._clientColumnsStartAt1 = true; - this._clientPathsAreURIs = false; - - this._isServer = typeof obsolete_isServer === 'boolean' ? obsolete_isServer : false; - - this.onClose(() => { - this.shutdown(); - }); - this.onError((_error) => { - this.shutdown(); - }); - } - - public setDebuggerPathFormat(format: string) { - this._debuggerPathsAreURIs = format !== 'path'; - } - - public setDebuggerLinesStartAt1(enable: boolean) { - this._debuggerLinesStartAt1 = enable; - } - - public setDebuggerColumnsStartAt1(enable: boolean) { - this._debuggerColumnsStartAt1 = enable; - } - - public setRunAsServer(enable: boolean) { - this._isServer = enable; - } - - public shutdown(): void { - if (this._isServer) { - // shutdown ignored in server mode - } else { - // TODO@AW - /* - // wait a bit before shutting down - setTimeout(() => { - process.exit(0); - }, 100); - */ - } - } - - protected sendErrorResponse(response: DebugProtocol.Response, codeOrMessage: number | DebugProtocol.Message, format?: string, variables?: any, dest: ErrorDestination = ErrorDestination.User): void { - - let msg: DebugProtocol.Message; - if (typeof codeOrMessage === 'number') { - msg = { - id: codeOrMessage, - format: format - }; - if (variables) { - msg.variables = variables; - } - if (dest & ErrorDestination.User) { - msg.showUser = true; - } - if (dest & ErrorDestination.Telemetry) { - msg.sendTelemetry = true; - } - } else { - msg = codeOrMessage; - } - - response.success = false; - response.message = DebugSession.formatPII(msg.format, true, msg.variables); - if (!response.body) { - response.body = {}; - } - response.body.error = msg; - - this.sendResponse(response); - } - - public runInTerminalRequest(args: DebugProtocol.RunInTerminalRequestArguments, timeout: number, cb: (response: DebugProtocol.Response) => void) { - this.sendRequest('runInTerminal', args, timeout, cb); - } - - protected dispatchRequest(request: DebugProtocol.Request): void { - - const response = new Response(request); - - try { - if (request.command === 'initialize') { - const args = request.arguments; - - if (typeof args.linesStartAt1 === 'boolean') { - this._clientLinesStartAt1 = args.linesStartAt1; - } - if (typeof args.columnsStartAt1 === 'boolean') { - this._clientColumnsStartAt1 = args.columnsStartAt1; - } - - if (args.pathFormat !== 'path') { - this.sendErrorResponse(response, 2018, 'debug adapter only supports native paths', null, ErrorDestination.Telemetry); - } else { - const initializeResponse = response; - initializeResponse.body = {}; - this.initializeRequest(initializeResponse, args); - } - - } else if (request.command === 'launch') { - this.launchRequest(response, request.arguments, request); - - } else if (request.command === 'attach') { - this.attachRequest(response, request.arguments, request); - - } else if (request.command === 'disconnect') { - this.disconnectRequest(response, request.arguments, request); - - } else if (request.command === 'terminate') { - this.terminateRequest(response, request.arguments, request); - - } else if (request.command === 'restart') { - this.restartRequest(response, request.arguments, request); - - } else if (request.command === 'setBreakpoints') { - this.setBreakPointsRequest(response, request.arguments, request); - - } else if (request.command === 'setFunctionBreakpoints') { - this.setFunctionBreakPointsRequest(response, request.arguments, request); - - } else if (request.command === 'setExceptionBreakpoints') { - this.setExceptionBreakPointsRequest(response, request.arguments, request); - - } else if (request.command === 'configurationDone') { - this.configurationDoneRequest(response, request.arguments, request); - - } else if (request.command === 'continue') { - this.continueRequest(response, request.arguments, request); - - } else if (request.command === 'next') { - this.nextRequest(response, request.arguments, request); - - } else if (request.command === 'stepIn') { - this.stepInRequest(response, request.arguments, request); - - } else if (request.command === 'stepOut') { - this.stepOutRequest(response, request.arguments, request); - - } else if (request.command === 'stepBack') { - this.stepBackRequest(response, request.arguments, request); - - } else if (request.command === 'reverseContinue') { - this.reverseContinueRequest(response, request.arguments, request); - - } else if (request.command === 'restartFrame') { - this.restartFrameRequest(response, request.arguments, request); - - } else if (request.command === 'goto') { - this.gotoRequest(response, request.arguments, request); - - } else if (request.command === 'pause') { - this.pauseRequest(response, request.arguments, request); - - } else if (request.command === 'stackTrace') { - this.stackTraceRequest(response, request.arguments, request); - - } else if (request.command === 'scopes') { - this.scopesRequest(response, request.arguments, request); - - } else if (request.command === 'variables') { - this.variablesRequest(response, request.arguments, request); - - } else if (request.command === 'setVariable') { - this.setVariableRequest(response, request.arguments, request); - - } else if (request.command === 'setExpression') { - this.setExpressionRequest(response, request.arguments, request); - - } else if (request.command === 'source') { - this.sourceRequest(response, request.arguments, request); - - } else if (request.command === 'threads') { - this.threadsRequest(response, request); - - } else if (request.command === 'terminateThreads') { - this.terminateThreadsRequest(response, request.arguments, request); - - } else if (request.command === 'evaluate') { - this.evaluateRequest(response, request.arguments, request); - - } else if (request.command === 'stepInTargets') { - this.stepInTargetsRequest(response, request.arguments, request); - - } else if (request.command === 'gotoTargets') { - this.gotoTargetsRequest(response, request.arguments, request); - - } else if (request.command === 'completions') { - this.completionsRequest(response, request.arguments, request); - - } else if (request.command === 'exceptionInfo') { - this.exceptionInfoRequest(response, request.arguments, request); - - } else if (request.command === 'loadedSources') { - this.loadedSourcesRequest(response, request.arguments, request); - - } else if (request.command === 'dataBreakpointInfo') { - this.dataBreakpointInfoRequest(response, request.arguments, request); - - } else if (request.command === 'setDataBreakpoints') { - this.setDataBreakpointsRequest(response, request.arguments, request); - - } else if (request.command === 'readMemory') { - this.readMemoryRequest(response, request.arguments, request); - - } else if (request.command === 'disassemble') { - this.disassembleRequest(response, request.arguments, request); - - } else if (request.command === 'cancel') { - this.cancelRequest(response, request.arguments, request); - - } else if (request.command === 'breakpointLocations') { - this.breakpointLocationsRequest(response, request.arguments, request); - - } else { - this.customRequest(request.command, response, request.arguments, request); - } - } catch (e) { - this.sendErrorResponse(response, 1104, '{_stack}', { _exception: e.message, _stack: e.stack }, ErrorDestination.Telemetry); - } - } - - protected initializeRequest(response: DebugProtocol.InitializeResponse, _args: DebugProtocol.InitializeRequestArguments): void { - - response.body = response.body || {}; - - // This default debug adapter does not support conditional breakpoints. - response.body.supportsConditionalBreakpoints = false; - - // This default debug adapter does not support hit conditional breakpoints. - response.body.supportsHitConditionalBreakpoints = false; - - // This default debug adapter does not support function breakpoints. - response.body.supportsFunctionBreakpoints = false; - - // This default debug adapter implements the 'configurationDone' request. - response.body.supportsConfigurationDoneRequest = true; - - // This default debug adapter does not support hovers based on the 'evaluate' request. - response.body.supportsEvaluateForHovers = false; - - // This default debug adapter does not support the 'stepBack' request. - response.body.supportsStepBack = false; - - // This default debug adapter does not support the 'setVariable' request. - response.body.supportsSetVariable = false; - - // This default debug adapter does not support the 'restartFrame' request. - response.body.supportsRestartFrame = false; - - // This default debug adapter does not support the 'stepInTargets' request. - response.body.supportsStepInTargetsRequest = false; - - // This default debug adapter does not support the 'gotoTargets' request. - response.body.supportsGotoTargetsRequest = false; - - // This default debug adapter does not support the 'completions' request. - response.body.supportsCompletionsRequest = false; - - // This default debug adapter does not support the 'restart' request. - response.body.supportsRestartRequest = false; - - // This default debug adapter does not support the 'exceptionOptions' attribute on the 'setExceptionBreakpoints' request. - response.body.supportsExceptionOptions = false; - - // This default debug adapter does not support the 'format' attribute on the 'variables', 'evaluate', and 'stackTrace' request. - response.body.supportsValueFormattingOptions = false; - - // This debug adapter does not support the 'exceptionInfo' request. - response.body.supportsExceptionInfoRequest = false; - - // This debug adapter does not support the 'TerminateDebuggee' attribute on the 'disconnect' request. - response.body.supportTerminateDebuggee = false; - - // This debug adapter does not support delayed loading of stack frames. - response.body.supportsDelayedStackTraceLoading = false; - - // This debug adapter does not support the 'loadedSources' request. - response.body.supportsLoadedSourcesRequest = false; - - // This debug adapter does not support the 'logMessage' attribute of the SourceBreakpoint. - response.body.supportsLogPoints = false; - - // This debug adapter does not support the 'terminateThreads' request. - response.body.supportsTerminateThreadsRequest = false; - - // This debug adapter does not support the 'setExpression' request. - response.body.supportsSetExpression = false; - - // This debug adapter does not support the 'terminate' request. - response.body.supportsTerminateRequest = false; - - // This debug adapter does not support data breakpoints. - response.body.supportsDataBreakpoints = false; - - /** This debug adapter does not support the 'readMemory' request. */ - response.body.supportsReadMemoryRequest = false; - - /** The debug adapter does not support the 'disassemble' request. */ - response.body.supportsDisassembleRequest = false; - - /** The debug adapter does not support the 'cancel' request. */ - response.body.supportsCancelRequest = false; - - /** The debug adapter does not support the 'breakpointLocations' request. */ - response.body.supportsBreakpointLocationsRequest = false; - - this.sendResponse(response); - } - - protected disconnectRequest(response: DebugProtocol.DisconnectResponse, _args: DebugProtocol.DisconnectArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - this.shutdown(); - } - - protected launchRequest(response: DebugProtocol.LaunchResponse, _args: DebugProtocol.LaunchRequestArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected attachRequest(response: DebugProtocol.AttachResponse, _args: DebugProtocol.AttachRequestArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected terminateRequest(response: DebugProtocol.TerminateResponse, _args: DebugProtocol.TerminateArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected restartRequest(response: DebugProtocol.RestartResponse, _args: DebugProtocol.RestartArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, _args: DebugProtocol.SetBreakpointsArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected setFunctionBreakPointsRequest(response: DebugProtocol.SetFunctionBreakpointsResponse, _args: DebugProtocol.SetFunctionBreakpointsArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected setExceptionBreakPointsRequest(response: DebugProtocol.SetExceptionBreakpointsResponse, _args: DebugProtocol.SetExceptionBreakpointsArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected configurationDoneRequest(response: DebugProtocol.ConfigurationDoneResponse, _args: DebugProtocol.ConfigurationDoneArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected continueRequest(response: DebugProtocol.ContinueResponse, _args: DebugProtocol.ContinueArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected nextRequest(response: DebugProtocol.NextResponse, _args: DebugProtocol.NextArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected stepInRequest(response: DebugProtocol.StepInResponse, _args: DebugProtocol.StepInArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected stepOutRequest(response: DebugProtocol.StepOutResponse, _args: DebugProtocol.StepOutArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected stepBackRequest(response: DebugProtocol.StepBackResponse, _args: DebugProtocol.StepBackArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected reverseContinueRequest(response: DebugProtocol.ReverseContinueResponse, _args: DebugProtocol.ReverseContinueArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected restartFrameRequest(response: DebugProtocol.RestartFrameResponse, _args: DebugProtocol.RestartFrameArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected gotoRequest(response: DebugProtocol.GotoResponse, _args: DebugProtocol.GotoArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected pauseRequest(response: DebugProtocol.PauseResponse, _args: DebugProtocol.PauseArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected sourceRequest(response: DebugProtocol.SourceResponse, _args: DebugProtocol.SourceArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected threadsRequest(response: DebugProtocol.ThreadsResponse, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected terminateThreadsRequest(response: DebugProtocol.TerminateThreadsResponse, _args: DebugProtocol.TerminateThreadsArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected stackTraceRequest(response: DebugProtocol.StackTraceResponse, _args: DebugProtocol.StackTraceArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected scopesRequest(response: DebugProtocol.ScopesResponse, _args: DebugProtocol.ScopesArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected variablesRequest(response: DebugProtocol.VariablesResponse, _args: DebugProtocol.VariablesArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected setVariableRequest(response: DebugProtocol.SetVariableResponse, _args: DebugProtocol.SetVariableArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected setExpressionRequest(response: DebugProtocol.SetExpressionResponse, _args: DebugProtocol.SetExpressionArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected evaluateRequest(response: DebugProtocol.EvaluateResponse, _args: DebugProtocol.EvaluateArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected stepInTargetsRequest(response: DebugProtocol.StepInTargetsResponse, _args: DebugProtocol.StepInTargetsArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected gotoTargetsRequest(response: DebugProtocol.GotoTargetsResponse, _args: DebugProtocol.GotoTargetsArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected completionsRequest(response: DebugProtocol.CompletionsResponse, _args: DebugProtocol.CompletionsArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected exceptionInfoRequest(response: DebugProtocol.ExceptionInfoResponse, _args: DebugProtocol.ExceptionInfoArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected loadedSourcesRequest(response: DebugProtocol.LoadedSourcesResponse, _args: DebugProtocol.LoadedSourcesArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected dataBreakpointInfoRequest(response: DebugProtocol.DataBreakpointInfoResponse, _args: DebugProtocol.DataBreakpointInfoArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected setDataBreakpointsRequest(response: DebugProtocol.SetDataBreakpointsResponse, _args: DebugProtocol.SetDataBreakpointsArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected readMemoryRequest(response: DebugProtocol.ReadMemoryResponse, _args: DebugProtocol.ReadMemoryArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected disassembleRequest(response: DebugProtocol.DisassembleResponse, _args: DebugProtocol.DisassembleArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected cancelRequest(response: DebugProtocol.CancelResponse, _args: DebugProtocol.CancelArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected breakpointLocationsRequest(response: DebugProtocol.BreakpointLocationsResponse, _args: DebugProtocol.BreakpointLocationsArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - /** - * Override this hook to implement custom requests. - */ - protected customRequest(_command: string, response: DebugProtocol.Response, _args: any, _request?: DebugProtocol.Request): void { - this.sendErrorResponse(response, 1014, 'unrecognized request', null, ErrorDestination.Telemetry); - } - - //---- protected ------------------------------------------------------------------------------------------------- - - protected convertClientLineToDebugger(line: number): number { - if (this._debuggerLinesStartAt1) { - return this._clientLinesStartAt1 ? line : line + 1; - } - return this._clientLinesStartAt1 ? line - 1 : line; - } - - protected convertDebuggerLineToClient(line: number): number { - if (this._debuggerLinesStartAt1) { - return this._clientLinesStartAt1 ? line : line - 1; - } - return this._clientLinesStartAt1 ? line + 1 : line; - } - - protected convertClientColumnToDebugger(column: number): number { - if (this._debuggerColumnsStartAt1) { - return this._clientColumnsStartAt1 ? column : column + 1; - } - return this._clientColumnsStartAt1 ? column - 1 : column; - } - - protected convertDebuggerColumnToClient(column: number): number { - if (this._debuggerColumnsStartAt1) { - return this._clientColumnsStartAt1 ? column : column - 1; - } - return this._clientColumnsStartAt1 ? column + 1 : column; - } - - protected convertClientPathToDebugger(clientPath: string): string { - if (this._clientPathsAreURIs !== this._debuggerPathsAreURIs) { - if (this._clientPathsAreURIs) { - return DebugSession.uri2path(clientPath); - } else { - return DebugSession.path2uri(clientPath); - } - } - return clientPath; - } - - protected convertDebuggerPathToClient(debuggerPath: string): string { - if (this._debuggerPathsAreURIs !== this._clientPathsAreURIs) { - if (this._debuggerPathsAreURIs) { - return DebugSession.uri2path(debuggerPath); - } else { - return DebugSession.path2uri(debuggerPath); - } - } - return debuggerPath; - } - - //---- private ------------------------------------------------------------------------------- - - private static path2uri(path: string): string { - - path = encodeURI(path); - - let uri = new URL(`file:`); // ignore 'path' for now - uri.pathname = path; // now use 'path' to get the correct percent encoding (see https://url.spec.whatwg.org) - return uri.toString(); - } - - private static uri2path(sourceUri: string): string { - - let uri = new URL(sourceUri); - let s = decodeURIComponent(uri.pathname); - return s; - } - - private static _formatPIIRegexp = /{([^}]+)}/g; - - /* - * If argument starts with '_' it is OK to send its value to telemetry. - */ - private static formatPII(format: string, excludePII: boolean, args?: { [key: string]: string }): string { - return format.replace(DebugSession._formatPIIRegexp, function (match, paramName) { - if (excludePII && paramName.length > 0 && paramName[0] !== '_') { - return match; - } - return args && args[paramName] && args.hasOwnProperty(paramName) ? - args[paramName] : - match; - }); - } -} - -//--------------------------------------------------------------------------- - -export class Handles { - - private START_HANDLE = 1000; - - private _nextHandle: number; - private _handleMap = new Map(); - - public constructor(startHandle?: number) { - this._nextHandle = typeof startHandle === 'number' ? startHandle : this.START_HANDLE; - } - - public reset(): void { - this._nextHandle = this.START_HANDLE; - this._handleMap = new Map(); - } - - public create(value: T): number { - const handle = this._nextHandle++; - this._handleMap.set(handle, value); - return handle; - } - - public get(handle: number, dflt?: T): T | undefined { - return this._handleMap.get(handle) || dflt; - } -} - -//--------------------------------------------------------------------------- - -class MockConfigurationProvider implements vscode.DebugConfigurationProvider { - - /** - * Massage a debug configuration just before a debug session is being launched, - * e.g. add all missing attributes to the debug configuration. - */ - resolveDebugConfiguration(_folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration, _token?: vscode.CancellationToken): vscode.ProviderResult { - - // if launch.json is missing or empty - if (!config.type && !config.request && !config.name) { - const editor = vscode.window.activeTextEditor; - if (editor && editor.document.languageId === 'markdown') { - config.type = 'mock'; - config.name = 'Launch'; - config.request = 'launch'; - config.program = '${file}'; - config.stopOnEntry = true; - } - } - - if (!config.program) { - return vscode.window.showInformationMessage('Cannot find a program to debug').then(_ => { - return undefined; // abort launch - }); - } - - return config; - } -} - -export class MockDebugAdapterDescriptorFactory implements vscode.DebugAdapterDescriptorFactory { - - constructor(private memfs: MemFS) { - } - - createDebugAdapterDescriptor(_session: vscode.DebugSession, _executable: vscode.DebugAdapterExecutable | undefined): vscode.ProviderResult { - return new vscode.DebugAdapterInlineImplementation(new MockDebugSession(this.memfs)); - } -} - -function basename(path: string): string { - const pos = path.lastIndexOf('/'); - if (pos >= 0) { - return path.substring(pos + 1); - } - return path; -} - -function timeout(ms: number) { - return new Promise(resolve => setTimeout(resolve, ms)); -} - -/** - * This interface describes the mock-debug specific launch attributes - * (which are not part of the Debug Adapter Protocol). - * The schema for these attributes lives in the package.json of the mock-debug extension. - * The interface should always match this schema. - */ -interface LaunchRequestArguments extends DebugProtocol.LaunchRequestArguments { - /** An absolute path to the "program" to debug. */ - program: string; - /** Automatically stop target after launch. If not specified, target does not stop. */ - stopOnEntry?: boolean; - /** enable logging the Debug Adapter Protocol */ - trace?: boolean; -} - -export class MockDebugSession extends DebugSession { - - // we don't support multiple threads, so we can use a hardcoded ID for the default thread - private static THREAD_ID = 1; - - // a Mock runtime (or debugger) - private _runtime: MockRuntime; - - private _variableHandles = new Handles(); - - //private _configurationDone = new Subject(); - - private promiseResolve?: () => void; - private _configurationDone = new Promise((r, _e) => { - this.promiseResolve = r; - setTimeout(r, 1000); - }); - - private _cancelationTokens = new Map(); - private _isLongrunning = new Map(); - - /** - * Creates a new debug adapter that is used for one debug session. - * We configure the default implementation of a debug adapter here. - */ - public constructor(memfs: MemFS) { - - super(); - - // this debugger uses zero-based lines and columns - this.setDebuggerLinesStartAt1(false); - this.setDebuggerColumnsStartAt1(false); - - this._runtime = new MockRuntime(memfs); - - // setup event handlers - this._runtime.onStopOnEntry(() => { - this.sendEvent(new StoppedEvent('entry', MockDebugSession.THREAD_ID)); - }); - this._runtime.onStopOnStep(() => { - this.sendEvent(new StoppedEvent('step', MockDebugSession.THREAD_ID)); - }); - this._runtime.onStopOnBreakpoint(() => { - this.sendEvent(new StoppedEvent('breakpoint', MockDebugSession.THREAD_ID)); - }); - this._runtime.onStopOnDataBreakpoint(() => { - this.sendEvent(new StoppedEvent('data breakpoint', MockDebugSession.THREAD_ID)); - }); - this._runtime.onStopOnException(() => { - this.sendEvent(new StoppedEvent('exception', MockDebugSession.THREAD_ID)); - }); - this._runtime.onBreakpointValidated((bp: MockBreakpoint) => { - this.sendEvent(new BreakpointEvent('changed', { verified: bp.verified, id: bp.id })); - }); - this._runtime.onOutput(oe => { - const e: DebugProtocol.OutputEvent = new OutputEvent(`${oe.text}\n`); - e.body.source = this.createSource(oe.filePath); - e.body.line = this.convertDebuggerLineToClient(oe.line); - e.body.column = this.convertDebuggerColumnToClient(oe.column); - this.sendEvent(e); - }); - this._runtime.onEnd(() => { - this.sendEvent(new TerminatedEvent()); - }); - } - - /** - * The 'initialize' request is the first request called by the frontend - * to interrogate the features the debug adapter provides. - */ - protected initializeRequest(response: DebugProtocol.InitializeResponse, _args: DebugProtocol.InitializeRequestArguments): void { - - // build and return the capabilities of this debug adapter: - response.body = response.body || {}; - - // the adapter implements the configurationDoneRequest. - response.body.supportsConfigurationDoneRequest = true; - - // make VS Code to use 'evaluate' when hovering over source - response.body.supportsEvaluateForHovers = true; - - // make VS Code to show a 'step back' button - response.body.supportsStepBack = true; - - // make VS Code to support data breakpoints - response.body.supportsDataBreakpoints = true; - - // make VS Code to support completion in REPL - response.body.supportsCompletionsRequest = true; - response.body.completionTriggerCharacters = ['.', '[']; - - // make VS Code to send cancelRequests - response.body.supportsCancelRequest = true; - - // make VS Code send the breakpointLocations request - response.body.supportsBreakpointLocationsRequest = true; - - this.sendResponse(response); - - // since this debug adapter can accept configuration requests like 'setBreakpoint' at any time, - // we request them early by sending an 'initializeRequest' to the frontend. - // The frontend will end the configuration sequence by calling 'configurationDone' request. - this.sendEvent(new InitializedEvent()); - } - - /** - * Called at the end of the configuration sequence. - * Indicates that all breakpoints etc. have been sent to the DA and that the 'launch' can start. - */ - protected configurationDoneRequest(response: DebugProtocol.ConfigurationDoneResponse, args: DebugProtocol.ConfigurationDoneArguments): void { - super.configurationDoneRequest(response, args); - - // notify the launchRequest that configuration has finished - //this._configurationDone.notify(); - if (this.promiseResolve) { - this.promiseResolve(); - } - } - - protected async launchRequest(response: DebugProtocol.LaunchResponse, args: LaunchRequestArguments) { - - // make sure to 'Stop' the buffered logging if 'trace' is not set - //logger.setup(args.trace ? Logger.LogLevel.Verbose : Logger.LogLevel.Stop, false); - - // wait until configuration has finished (and configurationDoneRequest has been called) - await this._configurationDone; - - // start the program in the runtime - this._runtime.start(`memfs:${args.program}`, !!args.stopOnEntry); - - this.sendResponse(response); - } - - protected setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments): void { - - const path = args.source.path; - const clientLines = args.lines || []; - - // clear all breakpoints for this file - this._runtime.clearBreakpoints(path); - - // set and verify breakpoint locations - const actualBreakpoints = clientLines.map(l => { - let { verified, line, id } = this._runtime.setBreakPoint(path, this.convertClientLineToDebugger(l)); - const bp = new Breakpoint(verified, this.convertDebuggerLineToClient(line)); - bp.id = id; - return bp; - }); - - // send back the actual breakpoint positions - response.body = { - breakpoints: actualBreakpoints - }; - this.sendResponse(response); - } - - protected breakpointLocationsRequest(response: DebugProtocol.BreakpointLocationsResponse, args: DebugProtocol.BreakpointLocationsArguments, _request?: DebugProtocol.Request): void { - - if (args.source.path) { - const bps = this._runtime.getBreakpoints(args.source.path, this.convertClientLineToDebugger(args.line)); - response.body = { - breakpoints: bps.map(col => { - return { - line: args.line, - column: this.convertDebuggerColumnToClient(col) - }; - }) - }; - } else { - response.body = { - breakpoints: [] - }; - } - this.sendResponse(response); - } - - protected threadsRequest(response: DebugProtocol.ThreadsResponse): void { - - // runtime supports no threads so just return a default thread. - response.body = { - threads: [ - new Thread(MockDebugSession.THREAD_ID, 'thread 1') - ] - }; - this.sendResponse(response); - } - - protected stackTraceRequest(response: DebugProtocol.StackTraceResponse, args: DebugProtocol.StackTraceArguments): void { - - const startFrame = typeof args.startFrame === 'number' ? args.startFrame : 0; - const maxLevels = typeof args.levels === 'number' ? args.levels : 1000; - const endFrame = startFrame + maxLevels; - - const stk = this._runtime.stack(startFrame, endFrame); - - response.body = { - stackFrames: stk.frames.map(f => new StackFrame(f.index, f.name, this.createSource(f.file), this.convertDebuggerLineToClient(f.line))), - totalFrames: stk.count - }; - this.sendResponse(response); - } - - protected scopesRequest(response: DebugProtocol.ScopesResponse, _args: DebugProtocol.ScopesArguments): void { - - response.body = { - scopes: [ - new Scope('Local', this._variableHandles.create('local'), false), - new Scope('Global', this._variableHandles.create('global'), true) - ] - }; - this.sendResponse(response); - } - - protected async variablesRequest(response: DebugProtocol.VariablesResponse, args: DebugProtocol.VariablesArguments, request?: DebugProtocol.Request) { - - const variables: DebugProtocol.Variable[] = []; - - if (this._isLongrunning.get(args.variablesReference)) { - // long running - - if (request) { - this._cancelationTokens.set(request.seq, false); - } - - for (let i = 0; i < 100; i++) { - await timeout(1000); - variables.push({ - name: `i_${i}`, - type: 'integer', - value: `${i}`, - variablesReference: 0 - }); - if (request && this._cancelationTokens.get(request.seq)) { - break; - } - } - - if (request) { - this._cancelationTokens.delete(request.seq); - } - - } else { - - const id = this._variableHandles.get(args.variablesReference); - - if (id) { - variables.push({ - name: id + '_i', - type: 'integer', - value: '123', - variablesReference: 0 - }); - variables.push({ - name: id + '_f', - type: 'float', - value: '3.14', - variablesReference: 0 - }); - variables.push({ - name: id + '_s', - type: 'string', - value: 'hello world', - variablesReference: 0 - }); - variables.push({ - name: id + '_o', - type: 'object', - value: 'Object', - variablesReference: this._variableHandles.create(id + '_o') - }); - - // cancelation support for long running requests - const nm = id + '_long_running'; - const ref = this._variableHandles.create(id + '_lr'); - variables.push({ - name: nm, - type: 'object', - value: 'Object', - variablesReference: ref - }); - this._isLongrunning.set(ref, true); - } - } - - response.body = { - variables: variables - }; - this.sendResponse(response); - } - - protected continueRequest(response: DebugProtocol.ContinueResponse, _args: DebugProtocol.ContinueArguments): void { - this._runtime.continue(); - this.sendResponse(response); - } - - protected reverseContinueRequest(response: DebugProtocol.ReverseContinueResponse, _args: DebugProtocol.ReverseContinueArguments): void { - this._runtime.continue(true); - this.sendResponse(response); - } - - protected nextRequest(response: DebugProtocol.NextResponse, _args: DebugProtocol.NextArguments): void { - this._runtime.step(); - this.sendResponse(response); - } - - protected stepBackRequest(response: DebugProtocol.StepBackResponse, _args: DebugProtocol.StepBackArguments): void { - this._runtime.step(true); - this.sendResponse(response); - } - - protected evaluateRequest(response: DebugProtocol.EvaluateResponse, args: DebugProtocol.EvaluateArguments): void { - - let reply: string | undefined = undefined; - - if (args.context === 'repl') { - // 'evaluate' supports to create and delete breakpoints from the 'repl': - const matches = /new +([0-9]+)/.exec(args.expression); - if (matches && matches.length === 2) { - if (this._runtime.sourceFile) { - const mbp = this._runtime.setBreakPoint(this._runtime.sourceFile, this.convertClientLineToDebugger(parseInt(matches[1]))); - const bp = new Breakpoint(mbp.verified, this.convertDebuggerLineToClient(mbp.line), undefined, this.createSource(this._runtime.sourceFile)); - bp.id = mbp.id; - this.sendEvent(new BreakpointEvent('new', bp)); - reply = `breakpoint created`; - } - } else { - const matches = /del +([0-9]+)/.exec(args.expression); - if (matches && matches.length === 2) { - const mbp = this._runtime.sourceFile ? this._runtime.clearBreakPoint(this._runtime.sourceFile, this.convertClientLineToDebugger(parseInt(matches[1]))) : undefined; - if (mbp) { - const bp = new Breakpoint(false); - bp.id = mbp.id; - this.sendEvent(new BreakpointEvent('removed', bp)); - reply = `breakpoint deleted`; - } - } - } - } - - response.body = { - result: reply ? reply : `evaluate(context: '${args.context}', '${args.expression}')`, - variablesReference: 0 - }; - this.sendResponse(response); - } - - protected dataBreakpointInfoRequest(response: DebugProtocol.DataBreakpointInfoResponse, args: DebugProtocol.DataBreakpointInfoArguments): void { - - response.body = { - dataId: null, - description: 'cannot break on data access', - accessTypes: undefined, - canPersist: false - }; - - if (args.variablesReference && args.name) { - const id = this._variableHandles.get(args.variablesReference); - if (id && id.startsWith('global_')) { - response.body.dataId = args.name; - response.body.description = args.name; - response.body.accessTypes = ['read']; - response.body.canPersist = false; - } - } - - this.sendResponse(response); - } - - protected setDataBreakpointsRequest(response: DebugProtocol.SetDataBreakpointsResponse, args: DebugProtocol.SetDataBreakpointsArguments): void { - - // clear all data breakpoints - this._runtime.clearAllDataBreakpoints(); - - response.body = { - breakpoints: [] - }; - - for (let dbp of args.breakpoints) { - // assume that id is the "address" to break on - const ok = this._runtime.setDataBreakpoint(dbp.dataId); - response.body.breakpoints.push({ - verified: ok - }); - } - - this.sendResponse(response); - } - - protected completionsRequest(response: DebugProtocol.CompletionsResponse, _args: DebugProtocol.CompletionsArguments): void { - - response.body = { - targets: [ - { - label: 'item 10', - sortText: '10' - }, - { - label: 'item 1', - sortText: '01' - }, - { - label: 'item 2', - sortText: '02' - } - ] - }; - this.sendResponse(response); - } - - protected cancelRequest(_response: DebugProtocol.CancelResponse, args: DebugProtocol.CancelArguments) { - if (args.requestId) { - this._cancelationTokens.set(args.requestId, true); - } - } - - //---- helpers - - private createSource(filePath: string): Source { - return new Source(basename(filePath), this.convertDebuggerPathToClient(filePath), undefined, undefined, 'mock-adapter-data'); - } -} - -//------------------------------------------------------------------------------------------------------------------------------------------ - - -/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ - -export interface MockBreakpoint { - id: number; - line: number; - verified: boolean; -} - -export interface MockOutputEvent { - text: string; - filePath: string; - line: number; - column: number; -} - -/** - * A Mock runtime with minimal debugger functionality. - */ -export class MockRuntime { - - private stopOnEntry = new vscode.EventEmitter(); - onStopOnEntry: vscode.Event = this.stopOnEntry.event; - - private stopOnStep = new vscode.EventEmitter(); - onStopOnStep: vscode.Event = this.stopOnStep.event; - - private stopOnBreakpoint = new vscode.EventEmitter(); - onStopOnBreakpoint: vscode.Event = this.stopOnBreakpoint.event; - - private stopOnDataBreakpoint = new vscode.EventEmitter(); - onStopOnDataBreakpoint: vscode.Event = this.stopOnDataBreakpoint.event; - - private stopOnException = new vscode.EventEmitter(); - onStopOnException: vscode.Event = this.stopOnException.event; - - private breakpointValidated = new vscode.EventEmitter(); - onBreakpointValidated: vscode.Event = this.breakpointValidated.event; - - private output = new vscode.EventEmitter(); - onOutput: vscode.Event = this.output.event; - - private end = new vscode.EventEmitter(); - onEnd: vscode.Event = this.end.event; - - - // the initial (and one and only) file we are 'debugging' - private _sourceFile?: string; - public get sourceFile() { - return this._sourceFile; - } - - // the contents (= lines) of the one and only file - private _sourceLines: string[] = []; - - // This is the next line that will be 'executed' - private _currentLine = 0; - - // maps from sourceFile to array of Mock breakpoints - private _breakPoints = new Map(); - - // since we want to send breakpoint events, we will assign an id to every event - // so that the frontend can match events with breakpoints. - private _breakpointId = 1; - - private _breakAddresses = new Set(); - - constructor(private memfs: MemFS) { - } - - /** - * Start executing the given program. - */ - public start(program: string, stopOnEntry: boolean) { - - this.loadSource(program); - this._currentLine = -1; - - if (this._sourceFile) { - this.verifyBreakpoints(this._sourceFile); - } - - if (stopOnEntry) { - // we step once - this.step(false, this.stopOnEntry); - } else { - // we just start to run until we hit a breakpoint or an exception - this.continue(); - } - } - - /** - * Continue execution to the end/beginning. - */ - public continue(reverse = false) { - this.run(reverse, undefined); - } - - /** - * Step to the next/previous non empty line. - */ - public step(reverse = false, event = this.stopOnStep) { - this.run(reverse, event); - } - - /** - * Returns a fake 'stacktrace' where every 'stackframe' is a word from the current line. - */ - public stack(startFrame: number, endFrame: number): { frames: any[], count: number } { - - const words = this._sourceLines[this._currentLine].trim().split(/\s+/); - - const frames = new Array(); - // every word of the current line becomes a stack frame. - for (let i = startFrame; i < Math.min(endFrame, words.length); i++) { - const name = words[i]; // use a word of the line as the stackframe name - frames.push({ - index: i, - name: `${name}(${i})`, - file: this._sourceFile, - line: this._currentLine - }); - } - return { - frames: frames, - count: words.length - }; - } - - public getBreakpoints(_path: string, line: number): number[] { - - const l = this._sourceLines[line]; - - let sawSpace = true; - const bps: number[] = []; - for (let i = 0; i < l.length; i++) { - if (l[i] !== ' ') { - if (sawSpace) { - bps.push(i); - sawSpace = false; - } - } else { - sawSpace = true; - } - } - - return bps; - } - - /* - * Set breakpoint in file with given line. - */ - public setBreakPoint(path: string, line: number): MockBreakpoint { - - const bp = { verified: false, line, id: this._breakpointId++ }; - let bps = this._breakPoints.get(path); - if (!bps) { - bps = new Array(); - this._breakPoints.set(path, bps); - } - bps.push(bp); - - this.verifyBreakpoints(path); - - return bp; - } - - /* - * Clear breakpoint in file with given line. - */ - public clearBreakPoint(path: string, line: number): MockBreakpoint | undefined { - let bps = this._breakPoints.get(path); - if (bps) { - const index = bps.findIndex(bp => bp.line === line); - if (index >= 0) { - const bp = bps[index]; - bps.splice(index, 1); - return bp; - } - } - return undefined; - } - - /* - * Clear all breakpoints for file. - */ - public clearBreakpoints(path: string): void { - this._breakPoints.delete(path); - } - - /* - * Set data breakpoint. - */ - public setDataBreakpoint(address: string): boolean { - if (address) { - this._breakAddresses.add(address); - return true; - } - return false; - } - - /* - * Clear all data breakpoints. - */ - public clearAllDataBreakpoints(): void { - this._breakAddresses.clear(); - } - - // private methods - - private loadSource(file: string) { - if (this._sourceFile !== file) { - this._sourceFile = file; - - const _textDecoder = new TextDecoder(); - - const uri = vscode.Uri.parse(file); - const content = _textDecoder.decode(this.memfs.readFile(uri)); - this._sourceLines = content.split('\n'); - - //this._sourceLines = readFileSync(this._sourceFile).toString().split('\n'); - } - } - - /** - * Run through the file. - * If stepEvent is specified only run a single step and emit the stepEvent. - */ - private run(reverse = false, stepEvent?: vscode.EventEmitter): void { - if (reverse) { - for (let ln = this._currentLine - 1; ln >= 0; ln--) { - if (this.fireEventsForLine(ln, stepEvent)) { - this._currentLine = ln; - return; - } - } - // no more lines: stop at first line - this._currentLine = 0; - this.stopOnEntry.fire(); - } else { - for (let ln = this._currentLine + 1; ln < this._sourceLines.length; ln++) { - if (this.fireEventsForLine(ln, stepEvent)) { - this._currentLine = ln; - return; - } - } - // no more lines: run to end - this.end.fire(); - } - } - - private verifyBreakpoints(path: string): void { - let bps = this._breakPoints.get(path); - if (bps) { - this.loadSource(path); - bps.forEach(bp => { - if (!bp.verified && bp.line < this._sourceLines.length) { - const srcLine = this._sourceLines[bp.line].trim(); - - // if a line is empty or starts with '+' we don't allow to set a breakpoint but move the breakpoint down - if (srcLine.length === 0 || srcLine.indexOf('+') === 0) { - bp.line++; - } - // if a line starts with '-' we don't allow to set a breakpoint but move the breakpoint up - if (srcLine.indexOf('-') === 0) { - bp.line--; - } - // don't set 'verified' to true if the line contains the word 'lazy' - // in this case the breakpoint will be verified 'lazy' after hitting it once. - if (srcLine.indexOf('lazy') < 0) { - bp.verified = true; - this.breakpointValidated.fire(bp); - } - } - }); - } - } - - /** - * Fire events if line has a breakpoint or the word 'exception' is found. - * Returns true is execution needs to stop. - */ - private fireEventsForLine(ln: number, stepEvent?: vscode.EventEmitter): boolean { - - const line = this._sourceLines[ln].trim(); - - // if 'log(...)' found in source -> send argument to debug console - const matches = /log\((.*)\)/.exec(line); - if (matches && matches.length === 2) { - if (this._sourceFile) { - this.output.fire({ text: matches[1], filePath: this._sourceFile, line: ln, column: matches.index }); - } - } - - // if a word in a line matches a data breakpoint, fire a 'dataBreakpoint' event - const words = line.split(' '); - for (let word of words) { - if (this._breakAddresses.has(word)) { - this.stopOnDataBreakpoint.fire(); - return true; - } - } - - // if word 'exception' found in source -> throw exception - if (line.indexOf('exception') >= 0) { - this.stopOnException.fire(); - return true; - } - - // is there a breakpoint? - const breakpoints = this._sourceFile ? this._breakPoints.get(this._sourceFile) : undefined; - if (breakpoints) { - const bps = breakpoints.filter(bp => bp.line === ln); - if (bps.length > 0) { - - // send 'stopped' event - this.stopOnBreakpoint.fire(); - - // the following shows the use of 'breakpoint' events to update properties of a breakpoint in the UI - // if breakpoint is not yet verified, verify it now and send a 'breakpoint' update event - if (!bps[0].verified) { - bps[0].verified = true; - this.breakpointValidated.fire(bps[0]); - } - return true; - } - } - - // non-empty line - if (stepEvent && line.length > 0) { - stepEvent.fire(); - return true; - } - - // nothing interesting found -> continue - return false; - } -} diff --git a/extensions/vscode-web-playground/src/memfs.ts b/extensions/vscode-web-playground/src/memfs.ts deleted file mode 100644 index 76c6f3f0e2e..00000000000 --- a/extensions/vscode-web-playground/src/memfs.ts +++ /dev/null @@ -1,449 +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 { - CancellationToken, - Disposable, - Event, - EventEmitter, - FileChangeEvent, - FileChangeType, - FileSearchOptions, - FileSearchProvider, - FileSearchQuery, - FileStat, - FileSystemError, - FileSystemProvider, - FileType, - Position, - Progress, - ProviderResult, - Range, - TextSearchComplete, - TextSearchOptions, - TextSearchQuery, - TextSearchProvider, - TextSearchResult, - Uri, - workspace, -} from 'vscode'; -import { largeTSFile, getImageFile, debuggableFile, windows1251File, gbkFile } from './exampleFiles'; - -export class File implements FileStat { - - type: FileType; - ctime: number; - mtime: number; - size: number; - - name: string; - data?: Uint8Array; - - constructor(public uri: Uri, name: string) { - this.type = FileType.File; - this.ctime = Date.now(); - this.mtime = Date.now(); - this.size = 0; - this.name = name; - } -} - -export class Directory implements FileStat { - - type: FileType; - ctime: number; - mtime: number; - size: number; - - name: string; - entries: Map; - - constructor(public uri: Uri, name: string) { - this.type = FileType.Directory; - this.ctime = Date.now(); - this.mtime = Date.now(); - this.size = 0; - this.name = name; - this.entries = new Map(); - } -} - -export type Entry = File | Directory; - -const textEncoder = new TextEncoder(); - -export class MemFS implements FileSystemProvider, FileSearchProvider, TextSearchProvider, Disposable { - static scheme = 'memfs'; - - private readonly disposable: Disposable; - - constructor() { - this.disposable = Disposable.from( - workspace.registerFileSystemProvider(MemFS.scheme, this, { isCaseSensitive: true }), - workspace.registerFileSearchProvider(MemFS.scheme, this), - workspace.registerTextSearchProvider(MemFS.scheme, this) - ); - } - - dispose() { - this.disposable?.dispose(); - } - - seed() { - this.createDirectory(Uri.parse(`memfs:/sample-folder/`)); - - // most common files types - this.writeFile(Uri.parse(`memfs:/sample-folder/large.ts`), textEncoder.encode(largeTSFile), { create: true, overwrite: true }); - this.writeFile(Uri.parse(`memfs:/sample-folder/file.txt`), textEncoder.encode('foo'), { create: true, overwrite: true }); - this.writeFile(Uri.parse(`memfs:/sample-folder/file.html`), textEncoder.encode('

    Hello

    '), { create: true, overwrite: true }); - this.writeFile(Uri.parse(`memfs:/sample-folder/file.js`), textEncoder.encode('console.log("JavaScript")'), { create: true, overwrite: true }); - this.writeFile(Uri.parse(`memfs:/sample-folder/file.json`), textEncoder.encode('{ "json": true }'), { create: true, overwrite: true }); - this.writeFile(Uri.parse(`memfs:/sample-folder/file.ts`), textEncoder.encode('console.log("TypeScript")'), { create: true, overwrite: true }); - this.writeFile(Uri.parse(`memfs:/sample-folder/file.css`), textEncoder.encode('* { color: green; }'), { create: true, overwrite: true }); - this.writeFile(Uri.parse(`memfs:/sample-folder/file.md`), textEncoder.encode(debuggableFile), { create: true, overwrite: true }); - this.writeFile(Uri.parse(`memfs:/sample-folder/file.xml`), textEncoder.encode(''), { create: true, overwrite: true }); - this.writeFile(Uri.parse(`memfs:/sample-folder/file.py`), textEncoder.encode('import base64, sys; base64.decode(open(sys.argv[1], "rb"), open(sys.argv[2], "wb"))'), { create: true, overwrite: true }); - this.writeFile(Uri.parse(`memfs:/sample-folder/file.yaml`), textEncoder.encode('- just: write something'), { create: true, overwrite: true }); - this.writeFile(Uri.parse(`memfs:/sample-folder/file.jpg`), getImageFile(), { create: true, overwrite: true }); - this.writeFile(Uri.parse(`memfs:/sample-folder/file.php`), textEncoder.encode(''), { create: true, overwrite: true }); - - // some more files & folders - this.createDirectory(Uri.parse(`memfs:/sample-folder/folder/`)); - this.createDirectory(Uri.parse(`memfs:/sample-folder/large/`)); - this.createDirectory(Uri.parse(`memfs:/sample-folder/xyz/`)); - this.createDirectory(Uri.parse(`memfs:/sample-folder/xyz/abc`)); - this.createDirectory(Uri.parse(`memfs:/sample-folder/xyz/def`)); - - this.writeFile(Uri.parse(`memfs:/sample-folder/folder/empty.txt`), new Uint8Array(0), { create: true, overwrite: true }); - this.writeFile(Uri.parse(`memfs:/sample-folder/folder/empty.foo`), new Uint8Array(0), { create: true, overwrite: true }); - this.writeFile(Uri.parse(`memfs:/sample-folder/folder/file.ts`), textEncoder.encode('let a:number = true; console.log(a);'), { create: true, overwrite: true }); - this.writeFile(Uri.parse(`memfs:/sample-folder/large/rnd.foo`), randomData(50000), { create: true, overwrite: true }); - this.writeFile(Uri.parse(`memfs:/sample-folder/xyz/UPPER.txt`), textEncoder.encode('UPPER'), { create: true, overwrite: true }); - this.writeFile(Uri.parse(`memfs:/sample-folder/xyz/upper.txt`), textEncoder.encode('upper'), { create: true, overwrite: true }); - this.writeFile(Uri.parse(`memfs:/sample-folder/xyz/def/foo.md`), textEncoder.encode('*MemFS*'), { create: true, overwrite: true }); - - // some files in different encodings - this.createDirectory(Uri.parse(`memfs:/sample-folder/encodings/`)); - this.writeFile( - Uri.parse(`memfs:/sample-folder/encodings/windows1251.txt`), - windows1251File, - { create: true, overwrite: true } - ); - this.writeFile( - Uri.parse(`memfs:/sample-folder/encodings/gbk.txt`), - gbkFile, - { create: true, overwrite: true } - ); - } - - root = new Directory(Uri.parse('memfs:/'), ''); - - // --- manage file metadata - - stat(uri: Uri): FileStat { - return this._lookup(uri, false); - } - - readDirectory(uri: Uri): [string, FileType][] { - const entry = this._lookupAsDirectory(uri, false); - let result: [string, FileType][] = []; - for (const [name, child] of entry.entries) { - result.push([name, child.type]); - } - return result; - } - - // --- manage file contents - - readFile(uri: Uri): Uint8Array { - const data = this._lookupAsFile(uri, false).data; - if (data) { - return data; - } - throw FileSystemError.FileNotFound(); - } - - writeFile(uri: Uri, content: Uint8Array, options: { create: boolean, overwrite: boolean }): void { - let basename = this._basename(uri.path); - let parent = this._lookupParentDirectory(uri); - let entry = parent.entries.get(basename); - if (entry instanceof Directory) { - throw FileSystemError.FileIsADirectory(uri); - } - if (!entry && !options.create) { - throw FileSystemError.FileNotFound(uri); - } - if (entry && options.create && !options.overwrite) { - throw FileSystemError.FileExists(uri); - } - if (!entry) { - entry = new File(uri, basename); - parent.entries.set(basename, entry); - this._fireSoon({ type: FileChangeType.Created, uri }); - } - entry.mtime = Date.now(); - entry.size = content.byteLength; - entry.data = content; - - this._fireSoon({ type: FileChangeType.Changed, uri }); - } - - // --- manage files/folders - - rename(oldUri: Uri, newUri: Uri, options: { overwrite: boolean }): void { - if (!options.overwrite && this._lookup(newUri, true)) { - throw FileSystemError.FileExists(newUri); - } - - let entry = this._lookup(oldUri, false); - let oldParent = this._lookupParentDirectory(oldUri); - - let newParent = this._lookupParentDirectory(newUri); - let newName = this._basename(newUri.path); - - oldParent.entries.delete(entry.name); - entry.name = newName; - newParent.entries.set(newName, entry); - - this._fireSoon( - { type: FileChangeType.Deleted, uri: oldUri }, - { type: FileChangeType.Created, uri: newUri } - ); - } - - delete(uri: Uri): void { - let dirname = uri.with({ path: this._dirname(uri.path) }); - let basename = this._basename(uri.path); - let parent = this._lookupAsDirectory(dirname, false); - if (!parent.entries.has(basename)) { - throw FileSystemError.FileNotFound(uri); - } - parent.entries.delete(basename); - parent.mtime = Date.now(); - parent.size -= 1; - this._fireSoon({ type: FileChangeType.Changed, uri: dirname }, { uri, type: FileChangeType.Deleted }); - } - - createDirectory(uri: Uri): void { - let basename = this._basename(uri.path); - let dirname = uri.with({ path: this._dirname(uri.path) }); - let parent = this._lookupAsDirectory(dirname, false); - - let entry = new Directory(uri, basename); - parent.entries.set(entry.name, entry); - parent.mtime = Date.now(); - parent.size += 1; - this._fireSoon({ type: FileChangeType.Changed, uri: dirname }, { type: FileChangeType.Created, uri }); - } - - // --- lookup - - private _lookup(uri: Uri, silent: false): Entry; - private _lookup(uri: Uri, silent: boolean): Entry | undefined; - private _lookup(uri: Uri, silent: boolean): Entry | undefined { - let parts = uri.path.split('/'); - let entry: Entry = this.root; - for (const part of parts) { - if (!part) { - continue; - } - let child: Entry | undefined; - if (entry instanceof Directory) { - child = entry.entries.get(part); - } - if (!child) { - if (!silent) { - throw FileSystemError.FileNotFound(uri); - } else { - return undefined; - } - } - entry = child; - } - return entry; - } - - private _lookupAsDirectory(uri: Uri, silent: boolean): Directory { - let entry = this._lookup(uri, silent); - if (entry instanceof Directory) { - return entry; - } - throw FileSystemError.FileNotADirectory(uri); - } - - private _lookupAsFile(uri: Uri, silent: boolean): File { - let entry = this._lookup(uri, silent); - if (entry instanceof File) { - return entry; - } - throw FileSystemError.FileIsADirectory(uri); - } - - private _lookupParentDirectory(uri: Uri): Directory { - const dirname = uri.with({ path: this._dirname(uri.path) }); - return this._lookupAsDirectory(dirname, false); - } - - // --- manage file events - - private _emitter = new EventEmitter(); - private _bufferedEvents: FileChangeEvent[] = []; - private _fireSoonHandle?: any; - - readonly onDidChangeFile: Event = this._emitter.event; - - watch(_resource: Uri): Disposable { - // ignore, fires for all changes... - return new Disposable(() => { }); - } - - private _fireSoon(...events: FileChangeEvent[]): void { - this._bufferedEvents.push(...events); - - if (this._fireSoonHandle) { - clearTimeout(this._fireSoonHandle); - } - - this._fireSoonHandle = setTimeout(() => { - this._emitter.fire(this._bufferedEvents); - this._bufferedEvents.length = 0; - }, 5); - } - - // --- path utils - - private _basename(path: string): string { - path = this._rtrim(path, '/'); - if (!path) { - return ''; - } - - return path.substr(path.lastIndexOf('/') + 1); - } - - private _dirname(path: string): string { - path = this._rtrim(path, '/'); - if (!path) { - return '/'; - } - - return path.substr(0, path.lastIndexOf('/')); - } - - private _rtrim(haystack: string, needle: string): string { - if (!haystack || !needle) { - return haystack; - } - - const needleLen = needle.length, - haystackLen = haystack.length; - - if (needleLen === 0 || haystackLen === 0) { - return haystack; - } - - let offset = haystackLen, - idx = -1; - - while (true) { - idx = haystack.lastIndexOf(needle, offset - 1); - if (idx === -1 || idx + needleLen !== offset) { - break; - } - if (idx === 0) { - return ''; - } - offset = idx; - } - - return haystack.substring(0, offset); - } - - private _getFiles(): Set { - const files = new Set(); - - this._doGetFiles(this.root, files); - - return files; - } - - private _doGetFiles(dir: Directory, files: Set): void { - dir.entries.forEach(entry => { - if (entry instanceof File) { - files.add(entry); - } else { - this._doGetFiles(entry, files); - } - }); - } - - private _convertSimple2RegExpPattern(pattern: string): string { - return pattern.replace(/[\-\\\{\}\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&').replace(/[\*]/g, '.*'); - } - - // --- search provider - - provideFileSearchResults(query: FileSearchQuery, _options: FileSearchOptions, _token: CancellationToken): ProviderResult { - return this._findFiles(query.pattern); - } - - private _findFiles(query: string | undefined): Uri[] { - const files = this._getFiles(); - const result: Uri[] = []; - - const pattern = query ? new RegExp(this._convertSimple2RegExpPattern(query)) : null; - - for (const file of files) { - if (!pattern || pattern.exec(file.name)) { - result.push(file.uri); - } - } - - return result; - } - - private _textDecoder = new TextDecoder(); - - provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress, _token: CancellationToken) { - const result: TextSearchComplete = { limitHit: false }; - - const files = this._findFiles(options.includes[0]); - if (files) { - for (const file of files) { - const content = this._textDecoder.decode(this.readFile(file)); - - const lines = content.split('\n'); - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - const index = line.indexOf(query.pattern); - if (index !== -1) { - progress.report({ - uri: file, - ranges: new Range(new Position(i, index), new Position(i, index + query.pattern.length)), - preview: { - text: line, - matches: new Range(new Position(0, index), new Position(0, index + query.pattern.length)) - } - }); - } - } - } - } - - return result; - } -} - -function randomData(lineCnt: number, lineLen = 155): Uint8Array { - let lines: string[] = []; - for (let i = 0; i < lineCnt; i++) { - let line = ''; - while (line.length < lineLen) { - line += Math.random().toString(2 + (i % 34)).substr(2); - } - lines.push(line.substr(0, lineLen)); - } - return textEncoder.encode(lines.join('\n')); -} diff --git a/extensions/vscode-web-playground/src/typings/ref.d.ts b/extensions/vscode-web-playground/src/typings/ref.d.ts deleted file mode 100644 index 9abc416f7e8..00000000000 --- a/extensions/vscode-web-playground/src/typings/ref.d.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. - *--------------------------------------------------------------------------------------------*/ - -/// -/// -/// -/// -/// diff --git a/extensions/vscode-web-playground/tsconfig.json b/extensions/vscode-web-playground/tsconfig.json deleted file mode 100644 index 633da7fad77..00000000000 --- a/extensions/vscode-web-playground/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "../shared.tsconfig.json", - "compilerOptions": { - "outDir": "./out", - "lib": [ - "dom", - "dom.iterable", - "es2018" - ] - }, - "include": [ - "src/**/*" - ] -} diff --git a/extensions/vscode-web-playground/yarn.lock b/extensions/vscode-web-playground/yarn.lock deleted file mode 100644 index b29fc8fc61d..00000000000 --- a/extensions/vscode-web-playground/yarn.lock +++ /dev/null @@ -1,109 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@types/mocha@2.2.43": - version "2.2.43" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.43.tgz#03c54589c43ad048cbcbfd63999b55d0424eec27" - integrity sha512-xNlAmH+lRJdUMXClMTI9Y0pRqIojdxfm7DHsIxoB2iTzu3fnPmSMEN8SsSx0cdwV36d02PWCWaDUoZPDSln+xw== - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= - -charenc@~0.0.1: - version "0.0.2" - resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" - integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= - -crypt@~0.0.1: - version "0.0.2" - resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" - integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= - -debug@^2.2.0: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^3.1.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== - dependencies: - ms "^2.1.1" - -is-buffer@~1.1.1: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - -lodash@^4.16.4: - version "4.17.15" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" - integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== - -md5@^2.1.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" - integrity sha1-U6s41f48iJG6RlMp6iP6wFQBJvk= - dependencies: - charenc "~0.0.1" - crypt "~0.0.1" - is-buffer "~1.1.1" - -minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== - -mkdirp@~0.5.1: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" - -mocha-junit-reporter@^1.17.0: - version "1.23.3" - resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.23.3.tgz#941e219dd759ed732f8641e165918aa8b167c981" - integrity sha512-ed8LqbRj1RxZfjt/oC9t12sfrWsjZ3gNnbhV1nuj9R/Jb5/P3Xb4duv2eCfCDMYH+fEu0mqca7m4wsiVjsxsvA== - dependencies: - debug "^2.2.0" - md5 "^2.1.0" - mkdirp "~0.5.1" - strip-ansi "^4.0.0" - xml "^1.0.0" - -mocha-multi-reporters@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/mocha-multi-reporters/-/mocha-multi-reporters-1.1.7.tgz#cc7f3f4d32f478520941d852abb64d9988587d82" - integrity sha1-zH8/TTL0eFIJQdhSq7ZNmYhYfYI= - dependencies: - debug "^3.1.0" - lodash "^4.16.4" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - -xml@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" - integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU= diff --git a/resources/serverless/code-web.js b/resources/serverless/code-web.js index 90d81b3fb9d..4a5f8da6b02 100644 --- a/resources/serverless/code-web.js +++ b/resources/serverless/code-web.js @@ -16,14 +16,19 @@ const opn = require('opn'); const minimist = require('minimist'); const fancyLog = require('fancy-log'); const ansiColors = require('ansi-colors'); +const remote = require('gulp-remote-retry-src'); +const vfs = require('vinyl-fs'); const extensions = require('../../build/lib/extensions'); const APP_ROOT = path.join(__dirname, '..', '..'); const BUILTIN_EXTENSIONS_ROOT = path.join(APP_ROOT, 'extensions'); const BUILTIN_MARKETPLACE_EXTENSIONS_ROOT = path.join(APP_ROOT, '.build', 'builtInExtensions'); +const WEB_DEV_EXTENSIONS_ROOT = path.join(APP_ROOT, '.build', 'builtInWebDevExtensions'); const WEB_MAIN = path.join(APP_ROOT, 'src', 'vs', 'code', 'browser', 'workbench', 'workbench-dev.html'); +const WEB_PLAYGROUND_VERSION = '0.0.1'; + const args = minimist(process.argv, { boolean: [ 'no-launch', @@ -72,9 +77,10 @@ async function getBuiltInExtensionInfos() { /** @type {Object.} */ const locations = {}; - const [localExtensions, marketplaceExtensions] = await Promise.all([ + const [localExtensions, marketplaceExtensions, webDevExtensions] = await Promise.all([ extensions.scanBuiltinExtensions(BUILTIN_EXTENSIONS_ROOT), extensions.scanBuiltinExtensions(BUILTIN_MARKETPLACE_EXTENSIONS_ROOT), + ensureWebDevExtensions().then(() => extensions.scanBuiltinExtensions(WEB_DEV_EXTENSIONS_ROOT)) ]); for (const ext of localExtensions) { allExtensions.push(ext); @@ -84,6 +90,10 @@ async function getBuiltInExtensionInfos() { allExtensions.push(ext); locations[ext.extensionPath] = path.join(BUILTIN_MARKETPLACE_EXTENSIONS_ROOT, ext.extensionPath); } + for (const ext of webDevExtensions) { + allExtensions.push(ext); + locations[ext.extensionPath] = path.join(WEB_DEV_EXTENSIONS_ROOT, ext.extensionPath); + } for (const ext of allExtensions) { if (ext.packageJSON.browser) { let mainFilePath = path.join(locations[ext.extensionPath], ext.packageJSON.browser); @@ -98,6 +108,42 @@ async function getBuiltInExtensionInfos() { return { extensions: allExtensions, locations }; } +async function ensureWebDevExtensions() { + + // Playground (https://github.com/microsoft/vscode-web-playground) + const webDevPlaygroundRoot = path.join(WEB_DEV_EXTENSIONS_ROOT, 'vscode-web-playground'); + const webDevPlaygroundExists = await exists(webDevPlaygroundRoot); + + let downloadPlayground = false; + if (webDevPlaygroundExists) { + try { + const webDevPlaygroundPackageJson = JSON.parse(((await readFile(path.join(webDevPlaygroundRoot, 'package.json'))).toString())); + if (webDevPlaygroundPackageJson.version !== WEB_PLAYGROUND_VERSION) { + downloadPlayground = true; + } + } catch (error) { + downloadPlayground = true; + } + } else { + downloadPlayground = true; + } + + if (downloadPlayground) { + if (args.verbose) { + fancyLog(`${ansiColors.magenta('Web Development extensions')}: Downloading vscode-web-playground to ${webDevPlaygroundRoot}`); + } + await new Promise((resolve, reject) => { + remote(['package.json', 'dist/extension.js', 'dist/extension.js.map'], { + base: 'https://raw.githubusercontent.com/microsoft/vscode-web-playground/main/' + }).pipe(vfs.dest(webDevPlaygroundRoot)).on('end', resolve).on('error', reject); + }); + } else { + if (args.verbose) { + fancyLog(`${ansiColors.magenta('Web Development extensions')}: Using existing vscode-web-playground in ${webDevPlaygroundRoot}`); + } + } +} + async function getDefaultExtensionInfos() { const extensions = []; From 494c209353f559eec7a170a1656096d606ee37e8 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 18 Aug 2020 09:21:45 +0200 Subject: [PATCH 304/736] mark toArray as deprecated/obsolete --- src/vs/base/common/arrays.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index cf9786a2076..d140e2ab635 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -590,6 +590,9 @@ export function asArray(x: T | T[]): T[] { return Array.isArray(x) ? x : [x]; } +/** + * @deprecated Use `Array.from` or `[...iter]` + */ export function toArray(iterable: IterableIterator): T[] { const result: T[] = []; for (let element of iterable) { From 651fb28e3cb983e1cbd2d4c1c653a7eb456f4cae Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 18 Aug 2020 09:49:48 +0200 Subject: [PATCH 305/736] web - rename resources/serverless => resources/web --- .vscode/launch.json | 2 +- build/gulpfile.hygiene.js | 4 ++-- package.json | 2 +- resources/{serverless => web}/callback.html | 0 resources/{serverless => web}/code-web.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename resources/{serverless => web}/callback.html (100%) rename resources/{serverless => web}/code-web.js (99%) diff --git a/.vscode/launch.json b/.vscode/launch.json index 1d966c8b508..33801a60ee3 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -241,7 +241,7 @@ "type": "node", "request": "launch", "name": "VS Code (Web)", - "program": "${workspaceFolder}/resources/serverless/code-web.js", + "program": "${workspaceFolder}/resources/web/code-web.js", "presentation": { "group": "0_vscode", "order": 2 diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js index fd1d8ceeed9..4952ee1c26d 100644 --- a/build/gulpfile.hygiene.js +++ b/build/gulpfile.hygiene.js @@ -119,12 +119,12 @@ const copyrightFilter = [ '!resources/linux/snap/snapcraft.yaml', '!resources/linux/snap/electron-launch', '!resources/win32/bin/code.js', + '!resources/web/code-web.js', '!resources/completions/**', '!extensions/markdown-language-features/media/highlight.css', '!extensions/html-language-features/server/src/modes/typescript/*', '!extensions/*/server/bin/*', - '!src/vs/editor/test/node/classification/typescript-test.ts', - '!resources/serverless/code-web.js' + '!src/vs/editor/test/node/classification/typescript-test.ts' ]; const jsHygieneFilter = [ diff --git a/package.json b/package.json index 9785d31d5a5..71a58a3e77e 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "valid-layers-check": "node build/lib/layersChecker.js", "strict-function-types-watch": "tsc --watch -p src/tsconfig.json --noEmit --strictFunctionTypes", "update-distro": "node build/npm/update-distro.js", - "web": "node resources/serverless/code-web.js", + "web": "node resources/web/code-web.js", "compile-web": "gulp compile-web --max_old_space_size=4095", "watch-web": "gulp watch-web --max_old_space_size=4095", "eslint": "eslint -c .eslintrc.json --rulesdir ./build/lib/eslint --ext .ts --ext .js ./src/vs ./extensions" diff --git a/resources/serverless/callback.html b/resources/web/callback.html similarity index 100% rename from resources/serverless/callback.html rename to resources/web/callback.html diff --git a/resources/serverless/code-web.js b/resources/web/code-web.js similarity index 99% rename from resources/serverless/code-web.js rename to resources/web/code-web.js index 4a5f8da6b02..bfe3f9f9a21 100644 --- a/resources/serverless/code-web.js +++ b/resources/web/code-web.js @@ -397,7 +397,7 @@ async function handleCallback(req, res, parsedUrl) { // add to map of known callbacks mapCallbackUriToRequestId.set(requestId, JSON.stringify({ scheme: vscodeScheme || 'code-oss', authority: vscodeAuthority, path: vscodePath, query, fragment: vscodeFragment })); - return serveFile(req, res, path.join(APP_ROOT, 'resources', 'serverless', 'callback.html'), { 'Content-Type': 'text/html' }); + return serveFile(req, res, path.join(APP_ROOT, 'resources', 'web', 'callback.html'), { 'Content-Type': 'text/html' }); } /** From 88c6e7b448dac750092f5f5bc51f03a39352f9b5 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 18 Aug 2020 09:56:55 +0200 Subject: [PATCH 306/736] debt - remove node.processEnv.ext.d.ts We will probably need to find a different solution for sandbox and should reduce exposure so that not everyone is seeing this hack --- src/typings/node.processEnv-ext.d.ts | 19 ------------------- .../node/externalTerminalService.ts | 14 +++++++++++++- 2 files changed, 13 insertions(+), 20 deletions(-) delete mode 100644 src/typings/node.processEnv-ext.d.ts diff --git a/src/typings/node.processEnv-ext.d.ts b/src/typings/node.processEnv-ext.d.ts deleted file mode 100644 index 4ca44ec5b37..00000000000 --- a/src/typings/node.processEnv-ext.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare namespace NodeJS { - - export interface Process { - - /** - * The lazy environment is a promise that resolves to `process.env` - * once the process is resolved. The use-case is VS Code running - * on Linux/macOS when being launched via a launcher. Then the env - * (as defined in .bashrc etc) isn't properly set and needs to be - * resolved lazy. - */ - lazyEnv: Thenable | undefined; - } -} diff --git a/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.ts b/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.ts index 067cc377d6c..99ac1f65419 100644 --- a/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.ts +++ b/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.ts @@ -18,6 +18,18 @@ import { DEFAULT_TERMINAL_OSX } from 'vs/workbench/contrib/externalTerminal/node const TERMINAL_TITLE = nls.localize('console.title', "VS Code Console"); +type LazyProcess = { + + /** + * The lazy environment is a promise that resolves to `process.env` + * once the process is resolved. The use-case is VS Code running + * on Linux/macOS when being launched via a launcher. Then the env + * (as defined in .bashrc etc) isn't properly set and needs to be + * resolved lazy. + */ + lazyEnv: Promise | undefined; +}; + export class WindowsExternalTerminalService implements IExternalTerminalService { public _serviceBrand: undefined; @@ -306,7 +318,7 @@ export class LinuxExternalTerminalService implements IExternalTerminalService { LinuxExternalTerminalService._DEFAULT_TERMINAL_LINUX_READY = new Promise(async r => { if (env.isLinux) { const isDebian = await pfs.exists('/etc/debian_version'); - await process.lazyEnv; + await (process as unknown as LazyProcess).lazyEnv; if (isDebian) { r('x-terminal-emulator'); } else if (process.env.DESKTOP_SESSION === 'gnome' || process.env.DESKTOP_SESSION === 'gnome-classic') { From b561a5e1ac666db1c8541f9e19c47af6ef318ba7 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 18 Aug 2020 09:59:33 +0200 Subject: [PATCH 307/736] debt - sync changes back from distro --- src/vs/code/browser/workbench/workbench-dev.html | 2 -- src/vs/code/browser/workbench/workbench.html | 1 - 2 files changed, 3 deletions(-) diff --git a/src/vs/code/browser/workbench/workbench-dev.html b/src/vs/code/browser/workbench/workbench-dev.html index 6196015ad64..55ae7a39871 100644 --- a/src/vs/code/browser/workbench/workbench-dev.html +++ b/src/vs/code/browser/workbench/workbench-dev.html @@ -30,7 +30,6 @@ diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html index 941f2e55399..f9c46333e2e 100644 --- a/src/vs/code/browser/workbench/workbench.html +++ b/src/vs/code/browser/workbench/workbench.html @@ -31,7 +31,6 @@ ` - })) - })) - }); - } -} diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index bec8d69a0b3..7e0fb78f8aa 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -3,39 +3,37 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; -import { Disposable, IDisposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; -import { URI, UriComponents } from 'vs/base/common/uri'; -import { notebookProviderExtensionPoint, notebookRendererExtensionPoint, INotebookEditorContribution } from 'vs/workbench/contrib/notebook/browser/extensionPoint'; -import { NotebookProviderInfo, NotebookEditorDescriptor } from 'vs/workbench/contrib/notebook/common/notebookProvider'; -import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.protocol'; -import { Emitter, Event } from 'vs/base/common/event'; -import { INotebookTextModel, INotebookRendererInfo, INotebookKernelInfo, CellOutputKind, ITransformedDisplayOutputDto, IDisplayOutput, ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, NOTEBOOK_DISPLAY_ORDER, sortMimeTypes, IOrderedMimeType, mimeTypeSupportedByCore, IOutputRenderRequestOutputInfo, IOutputRenderRequestCellInfo, NotebookCellOutputsSplice, ICellEditOperation, CellEditType, ICellInsertEdit, IOutputRenderResponse, IProcessedOutput, BUILTIN_RENDERER_ID, NotebookEditorPriority, INotebookKernelProvider, notebookDocumentFilterMatch, INotebookKernelInfo2, CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer'; -import { Iterable } from 'vs/base/common/iterator'; -import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { IEditorService, ICustomEditorViewTypesHandler, ICustomEditorInfo } from 'vs/workbench/services/editor/common/editorService'; -import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { INotebookService, IMainNotebookController } from 'vs/workbench/contrib/notebook/common/notebookService'; -import * as glob from 'vs/base/common/glob'; -import { basename } from 'vs/base/common/path'; -import { getActiveNotebookEditor, INotebookEditor, NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; -import { Memento } from 'vs/workbench/common/memento'; -import { StorageScope, IStorageService } from 'vs/platform/storage/common/storage'; -import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { generateUuid } from 'vs/base/common/uuid'; import { flatten } from 'vs/base/common/arrays'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { NotebookKernelProviderAssociationRegistry, updateNotebookKernelProvideAssociationSchema, NotebookViewTypesExtensionRegistry } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation'; -import { PureNotebookOutputRenderer } from 'vs/workbench/contrib/notebook/browser/notebookPureOutputRenderer'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Emitter, Event } from 'vs/base/common/event'; +import * as glob from 'vs/base/common/glob'; +import { Iterable } from 'vs/base/common/iterator'; +import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { basename } from 'vs/base/common/path'; +import { URI } from 'vs/base/common/uri'; import { RedoCommand, UndoCommand } from 'vs/editor/browser/editorExtensions'; import { CopyAction, CutAction, PasteAction } from 'vs/editor/contrib/clipboard/clipboard'; +import * as nls from 'vs/nls'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.protocol'; +import { Memento } from 'vs/workbench/common/memento'; +import { INotebookEditorContribution, notebookProviderExtensionPoint, notebookRendererExtensionPoint } from 'vs/workbench/contrib/notebook/browser/extensionPoint'; +import { getActiveNotebookEditor, INotebookEditor, NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { NotebookKernelProviderAssociationRegistry, NotebookViewTypesExtensionRegistry, updateNotebookKernelProvideAssociationSchema } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; +import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; +import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; +import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellEditType, CellOutputKind, CellUri, ICellEditOperation, IDisplayOutput, INotebookKernelInfo, INotebookKernelInfo2, INotebookKernelProvider, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, NotebookCellOutputsSplice, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer'; +import { NotebookEditorDescriptor, NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; +import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; +import { ICustomEditorInfo, ICustomEditorViewTypesHandler, IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; function MODEL_ID(resource: URI): string { return resource.toString(); @@ -234,7 +232,6 @@ export class NotebookService extends Disposable implements INotebookService, ICu declare readonly _serviceBrand: undefined; static mainthreadNotebookDocumentHandle: number = 0; private readonly _notebookProviders = new Map(); - private readonly _notebookRenderers = new Map(); private readonly _notebookKernels = new Map(); notebookProviderInfoStore: NotebookProviderInfoStore; notebookRenderersInfoStore: NotebookOutputRendererInfoStore = new NotebookOutputRendererInfoStore(); @@ -291,15 +288,18 @@ export class NotebookService extends Disposable implements INotebookService, ICu for (const extension of renderers) { for (const notebookContribution of extension.value) { + if (!notebookContribution.entrypoint) { // avoid crashing + console.error(`Cannot register renderer for ${extension.description.identifier.value} since it did not have an entrypoint. This is now required: https://github.com/microsoft/vscode/issues/102644`); + continue; + } + this.notebookRenderersInfoStore.add(new NotebookOutputRendererInfo({ id: notebookContribution.viewType, + extension: extension.description, + entrypoint: notebookContribution.entrypoint, displayName: notebookContribution.displayName, mimeTypes: notebookContribution.mimeTypes || [], })); - - if (notebookContribution.entrypoint) { - this._notebookRenderers.set(notebookContribution.viewType, new PureNotebookOutputRenderer(notebookContribution.viewType, notebookContribution.displayName, extension.description, notebookContribution.entrypoint)); - } } } @@ -553,19 +553,6 @@ export class NotebookService extends Disposable implements INotebookService, ICu this._onDidChangeViewTypes.fire(); } - registerNotebookRenderer(id: string, renderer: INotebookRendererInfo) { - this._notebookRenderers.set(id, renderer); - const staticInfo = this.notebookRenderersInfoStore.get(id); - - if (staticInfo) { - - } - } - - unregisterNotebookRenderer(id: string) { - this._notebookRenderers.delete(id); - } - registerNotebookKernel(notebook: INotebookKernelInfo): void { this._notebookKernels.set(notebook.id, notebook); this._onDidChangeKernels.fire(); @@ -664,9 +651,7 @@ export class NotebookService extends Disposable implements INotebookService, ICu } getRendererInfo(id: string): INotebookRendererInfo | undefined { - const renderer = this._notebookRenderers.get(id); - - return renderer; + return this.notebookRenderersInfoStore.get(id); } async resolveNotebook(viewType: string, uri: URI, forceReload: boolean, editorId?: string, backupId?: string): Promise { @@ -719,55 +704,11 @@ export class NotebookService extends Disposable implements INotebookService, ICu return this._models.get(modelId)?.model; } - private async _fillInTransformedOutputs( - renderers: Set, - requestItems: IOutputRenderRequestCellInfo[], - renderFunc: (rendererId: string, items: IOutputRenderRequestCellInfo[]) => Promise | undefined>, - lookUp: (key: T) => { outputs: IProcessedOutput[] } - ) { - for (const id of renderers) { - const requestsPerRenderer: IOutputRenderRequestCellInfo[] = requestItems.map(req => { - return { - key: req.key, - outputs: req.outputs.filter(output => output.handlerId === id) - }; - }); - - const response = await renderFunc(id, requestsPerRenderer); - - // mix the response with existing outputs, which will replace the picked transformed mimetype with resolved result - if (response) { - response.items.forEach(cellInfo => { - const cell = lookUp(cellInfo.key)!; - cellInfo.outputs.forEach(outputInfo => { - const output = cell.outputs[outputInfo.index]; - if (output.outputKind === CellOutputKind.Rich && output.orderedMimeTypes && output.orderedMimeTypes.length) { - output.orderedMimeTypes[0] = { - mimeType: outputInfo.mimeType, - isResolved: true, - rendererId: outputInfo.handlerId, - output: outputInfo.transformedOutput - }; - } - }); - }); - } - } - } - - async transformTextModelOutputs(textModel: NotebookTextModel) { - const renderers = new Set(); - - const cellMapping: Map = new Map(); - - const requestItems: IOutputRenderRequestCellInfo[] = []; + private async transformTextModelOutputs(textModel: NotebookTextModel) { for (let i = 0; i < textModel.cells.length; i++) { const cell = textModel.cells[i]; - cellMapping.set(cell.uri.fragment, cell); - const outputs = cell.outputs; - const outputRequest: IOutputRenderRequestOutputInfo[] = []; - outputs.forEach((output, index) => { + cell.outputs.forEach((output) => { if (output.outputKind === CellOutputKind.Rich) { // TODO no string[] casting const ret = this._transformMimeTypes(output, output.outputId, textModel.metadata.displayOrder as string[] || []); @@ -775,125 +716,43 @@ export class NotebookService extends Disposable implements INotebookService, ICu const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!; output.pickedMimeTypeIndex = pickedMimeTypeIndex; output.orderedMimeTypes = orderedMimeTypes; - - if (orderedMimeTypes[pickedMimeTypeIndex!].rendererId && orderedMimeTypes[pickedMimeTypeIndex].rendererId !== BUILTIN_RENDERER_ID) { - outputRequest.push({ index, handlerId: orderedMimeTypes[pickedMimeTypeIndex].rendererId!, mimeType: orderedMimeTypes[pickedMimeTypeIndex].mimeType, outputId: output.outputId }); - renderers.add(orderedMimeTypes[pickedMimeTypeIndex].rendererId!); - } } }); - - requestItems.push({ key: cell.uri, outputs: outputRequest }); } - - await this._fillInTransformedOutputs(renderers, requestItems, async (rendererId, items) => { - return await this._notebookRenderers.get(rendererId)?.render(textModel.uri, { items: items }); - }, (key: UriComponents) => { return cellMapping.get(URI.revive(key).fragment)!; }); - - textModel.updateRenderers([...renderers]); } - async transformEditsOutputs(textModel: NotebookTextModel, edits: ICellEditOperation[]) { - const renderers = new Set(); - const requestItems: IOutputRenderRequestCellInfo<[number, number]>[] = []; - - edits.forEach((edit, editIndex) => { + transformEditsOutputs(textModel: NotebookTextModel, edits: ICellEditOperation[]) { + edits.forEach((edit) => { if (edit.editType === CellEditType.Insert) { - edit.cells.forEach((cell, cellIndex) => { + edit.cells.forEach((cell) => { const outputs = cell.outputs; - const outputRequest: IOutputRenderRequestOutputInfo[] = []; - outputs.map((output, index) => { + outputs.map((output) => { if (output.outputKind === CellOutputKind.Rich) { const ret = this._transformMimeTypes(output, output.outputId, textModel.metadata.displayOrder as string[] || []); const orderedMimeTypes = ret.orderedMimeTypes!; const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!; output.pickedMimeTypeIndex = pickedMimeTypeIndex; output.orderedMimeTypes = orderedMimeTypes; - - if (orderedMimeTypes[pickedMimeTypeIndex!].rendererId && orderedMimeTypes[pickedMimeTypeIndex].rendererId !== BUILTIN_RENDERER_ID) { - outputRequest.push({ index, handlerId: orderedMimeTypes[pickedMimeTypeIndex].rendererId!, mimeType: orderedMimeTypes[pickedMimeTypeIndex].mimeType, output: output, outputId: output.outputId }); - renderers.add(orderedMimeTypes[pickedMimeTypeIndex].rendererId!); - } } }); - - requestItems.push({ key: [editIndex, cellIndex], outputs: outputRequest }); }); } }); - - await this._fillInTransformedOutputs<[number, number]>(renderers, requestItems, async (rendererId, items) => { - return await this._notebookRenderers.get(rendererId)?.render2<[number, number]>(textModel.uri, { items: items }); - }, (key: [number, number]) => { - return (edits[key[0]] as ICellInsertEdit).cells[key[1]]; - }); - - textModel.updateRenderers([...renderers]); } - async transformSpliceOutputs(textModel: NotebookTextModel, splices: NotebookCellOutputsSplice[]) { - const renderers = new Set(); - const requestItems: IOutputRenderRequestCellInfo[] = []; - - splices.forEach((splice, spliceIndex) => { + transformSpliceOutputs(textModel: NotebookTextModel, splices: NotebookCellOutputsSplice[]) { + splices.forEach((splice) => { const outputs = splice[2]; - const outputRequest: IOutputRenderRequestOutputInfo[] = []; - outputs.map((output, index) => { + outputs.map((output) => { if (output.outputKind === CellOutputKind.Rich) { const ret = this._transformMimeTypes(output, output.outputId, textModel.metadata.displayOrder as string[] || []); const orderedMimeTypes = ret.orderedMimeTypes!; const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!; output.pickedMimeTypeIndex = pickedMimeTypeIndex; output.orderedMimeTypes = orderedMimeTypes; - - if (orderedMimeTypes[pickedMimeTypeIndex!].rendererId && orderedMimeTypes[pickedMimeTypeIndex].rendererId !== BUILTIN_RENDERER_ID) { - outputRequest.push({ index, handlerId: orderedMimeTypes[pickedMimeTypeIndex].rendererId!, mimeType: orderedMimeTypes[pickedMimeTypeIndex].mimeType, output: output, outputId: output.outputId }); - renderers.add(orderedMimeTypes[pickedMimeTypeIndex].rendererId!); - } } }); - requestItems.push({ key: spliceIndex, outputs: outputRequest }); }); - - await this._fillInTransformedOutputs(renderers, requestItems, async (rendererId, items) => { - return await this._notebookRenderers.get(rendererId)?.render2(textModel.uri, { items: items }); - }, (key: number) => { - return { outputs: splices[key][2] }; - }); - - textModel.updateRenderers([...renderers]); - } - - async transformSingleOutput(textModel: NotebookTextModel, output: IProcessedOutput, rendererId: string, mimeType: string): Promise { - const items = [ - { - key: 0, - outputs: [ - { - index: 0, - outputId: generateUuid(), - handlerId: rendererId, - mimeType: mimeType, - output: output - } - ] - } - ]; - const response = await this._notebookRenderers.get(rendererId)?.render2(textModel.uri, { items: items }); - - if (response) { - textModel.updateRenderers([rendererId]); - const outputInfo = response.items[0].outputs[0]; - - return { - mimeType: outputInfo.mimeType, - isResolved: true, - rendererId: outputInfo.handlerId, - output: outputInfo.transformedOutput - }; - } - - return; } private _transformMimeTypes(output: IDisplayOutput, outputId: string, documentDisplayOrder: string[]): ITransformedDisplayOutputDto { @@ -911,14 +770,12 @@ export class NotebookService extends Disposable implements INotebookService, ICu orderMimeTypes.push({ mimeType: mimeType, - isResolved: false, rendererId: handler.id, }); for (let i = 1; i < handlers.length; i++) { orderMimeTypes.push({ mimeType: mimeType, - isResolved: false, rendererId: handlers[i].id }); } @@ -926,14 +783,12 @@ export class NotebookService extends Disposable implements INotebookService, ICu if (mimeTypeSupportedByCore(mimeType)) { orderMimeTypes.push({ mimeType: mimeType, - isResolved: false, rendererId: BUILTIN_RENDERER_ID }); } } else { orderMimeTypes.push({ mimeType: mimeType, - isResolved: false, rendererId: BUILTIN_RENDERER_ID }); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer.ts index 7e988c86142..56114e323a5 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IProcessedOutput, IRenderOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { IProcessedOutput, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookRegistry } from 'vs/workbench/contrib/notebook/browser/notebookRegistry'; import { onUnexpectedError } from 'vs/base/common/errors'; import { INotebookEditor, IOutputTransformContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; @@ -38,9 +38,7 @@ export class OutputRenderer { contentNode.innerText = `No renderer could be found for output. It has the following output type: ${output.outputKind}`; container.appendChild(contentNode); - return { - hasDynamicHeight: false - }; + return { type: RenderOutputType.None, hasDynamicHeight: false }; } render(output: IProcessedOutput, container: HTMLElement, preferredMimeType: string | undefined): IRenderOutput { diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform.ts index 7998129daa4..9e0c4ba4230 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IRenderOutput, CellOutputKind, IErrorOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { IRenderOutput, CellOutputKind, IErrorOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookRegistry } from 'vs/workbench/contrib/notebook/browser/notebookRegistry'; import * as DOM from 'vs/base/browser/dom'; import { RGBA, Color } from 'vs/base/common/color'; @@ -36,9 +36,7 @@ class ErrorTransform implements IOutputTransformContribution { } container.appendChild(traceback); DOM.addClasses(container, 'error'); - return { - hasDynamicHeight: false - }; + return { type: RenderOutputType.None, hasDynamicHeight: false }; } dispose(): void { diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts index 73a1fd4af7e..76ffcc3f352 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IRenderOutput, CellOutputKind, ITransformedDisplayOutputDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { IRenderOutput, CellOutputKind, ITransformedDisplayOutputDto, RenderOutputType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookRegistry } from 'vs/workbench/contrib/notebook/browser/notebookRegistry'; import * as DOM from 'vs/base/browser/dom'; import { INotebookEditor, IOutputTransformContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; @@ -46,10 +46,7 @@ class RichRenderer implements IOutputTransformContribution { const contentNode = document.createElement('p'); contentNode.innerText = `No data could be found for output.`; container.appendChild(contentNode); - - return { - hasDynamicHeight: false - }; + return { type: RenderOutputType.None, hasDynamicHeight: false }; } if (!preferredMimeType || !this._richMimeTypeRenderers.has(preferredMimeType)) { @@ -68,17 +65,14 @@ class RichRenderer implements IOutputTransformContribution { } container.appendChild(contentNode); - - return { - hasDynamicHeight: false - }; + return { type: RenderOutputType.None, hasDynamicHeight: false }; } const renderer = this._richMimeTypeRenderers.get(preferredMimeType); return renderer!(output, container); } - renderJSON(output: ITransformedDisplayOutputDto, container: HTMLElement) { + renderJSON(output: ITransformedDisplayOutputDto, container: HTMLElement): IRenderOutput { const data = output.data['application/json']; const str = JSON.stringify(data, null, '\t'); @@ -108,12 +102,10 @@ class RichRenderer implements IOutputTransformContribution { container.style.height = `${height + 16}px`; - return { - hasDynamicHeight: true - }; + return { type: RenderOutputType.None, hasDynamicHeight: true }; } - renderCode(output: ITransformedDisplayOutputDto, container: HTMLElement) { + renderCode(output: ITransformedDisplayOutputDto, container: HTMLElement): IRenderOutput { const data = output.data['text/x-javascript']; const str = (isArray(data) ? data.join('') : data) as string; @@ -143,87 +135,81 @@ class RichRenderer implements IOutputTransformContribution { container.style.height = `${height + 16}px`; - return { - hasDynamicHeight: true - }; + return { type: RenderOutputType.None, hasDynamicHeight: true }; } - renderJavaScript(output: ITransformedDisplayOutputDto, container: HTMLElement) { + renderJavaScript(output: ITransformedDisplayOutputDto, container: HTMLElement): IRenderOutput { const data = output.data['application/javascript']; const str = isArray(data) ? data.join('') : data; const scriptVal = ``; return { - shadowContent: scriptVal, + type: RenderOutputType.Html, + source: output, + htmlContent: scriptVal, hasDynamicHeight: false }; } - renderHTML(output: ITransformedDisplayOutputDto, container: HTMLElement) { + renderHTML(output: ITransformedDisplayOutputDto, container: HTMLElement): IRenderOutput { const data = output.data['text/html']; const str = (isArray(data) ? data.join('') : data) as string; return { - shadowContent: str, + type: RenderOutputType.Html, + source: output, + htmlContent: str, hasDynamicHeight: false }; - } - renderSVG(output: ITransformedDisplayOutputDto, container: HTMLElement) { + renderSVG(output: ITransformedDisplayOutputDto, container: HTMLElement): IRenderOutput { const data = output.data['image/svg+xml']; const str = (isArray(data) ? data.join('') : data) as string; return { - shadowContent: str, + type: RenderOutputType.Html, + source: output, + htmlContent: str, hasDynamicHeight: false }; } - renderMarkdown(output: ITransformedDisplayOutputDto, container: HTMLElement) { + renderMarkdown(output: ITransformedDisplayOutputDto, container: HTMLElement): IRenderOutput { const data = output.data['text/markdown']; const str = (isArray(data) ? data.join('') : data) as string; const mdOutput = document.createElement('div'); mdOutput.appendChild(this._mdRenderer.render({ value: str, isTrusted: true, supportThemeIcons: true }).element); container.appendChild(mdOutput); - return { - hasDynamicHeight: true - }; + return { type: RenderOutputType.None, hasDynamicHeight: true }; } - renderPNG(output: ITransformedDisplayOutputDto, container: HTMLElement) { + renderPNG(output: ITransformedDisplayOutputDto, container: HTMLElement): IRenderOutput { const image = document.createElement('img'); image.src = `data:image/png;base64,${output.data['image/png']}`; const display = document.createElement('div'); DOM.addClasses(display, 'display'); display.appendChild(image); container.appendChild(display); - return { - hasDynamicHeight: true - }; - + return { type: RenderOutputType.None, hasDynamicHeight: true }; } - renderJPEG(output: ITransformedDisplayOutputDto, container: HTMLElement) { + renderJPEG(output: ITransformedDisplayOutputDto, container: HTMLElement): IRenderOutput { const image = document.createElement('img'); image.src = `data:image/jpeg;base64,${output.data['image/jpeg']}`; const display = document.createElement('div'); DOM.addClasses(display, 'display'); display.appendChild(image); container.appendChild(display); - return { - hasDynamicHeight: true - }; + return { type: RenderOutputType.None, hasDynamicHeight: true }; } - renderPlainText(output: ITransformedDisplayOutputDto, container: HTMLElement) { + renderPlainText(output: ITransformedDisplayOutputDto, container: HTMLElement): IRenderOutput { const data = output.data['text/plain']; const str = (isArray(data) ? data.join('') : data) as string; const contentNode = DOM.$('.output-plaintext'); contentNode.appendChild(handleANSIOutput(str, this.themeService)); container.appendChild(contentNode); - return { - hasDynamicHeight: false - }; + return { type: RenderOutputType.None, hasDynamicHeight: false }; } dispose(): void { diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/streamTransform.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/streamTransform.ts index b3d7698c233..65f1826f750 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/streamTransform.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/streamTransform.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; -import { IRenderOutput, CellOutputKind, IStreamOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { IRenderOutput, CellOutputKind, IStreamOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookRegistry } from 'vs/workbench/contrib/notebook/browser/notebookRegistry'; import { INotebookEditor, IOutputTransformContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; @@ -18,10 +18,7 @@ class StreamRenderer implements IOutputTransformContribution { const contentNode = DOM.$('.output-stream'); contentNode.innerText = output.text; container.appendChild(contentNode); - return { - hasDynamicHeight: false - }; - + return { type: RenderOutputType.None, hasDynamicHeight: false }; } dispose(): void { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 7f37bd88b20..3a0eab9dda2 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -16,7 +16,7 @@ import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener' import { CELL_MARGIN, CELL_RUN_GUTTER, CODE_CELL_LEFT_MARGIN, CELL_OUTPUT_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; -import { CellOutputKind, IProcessedOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellOutputKind, IDisplayOutput, IInsetRenderOutput, INotebookRendererInfo, IProcessedOutput, ITransformedDisplayOutputDto, RenderOutputType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IWebviewService, WebviewElement, WebviewContentPurpose } from 'vs/workbench/contrib/webview/browser/webview'; import { asWebviewUri } from 'vs/workbench/contrib/webview/common/webviewUri'; @@ -88,12 +88,14 @@ export interface IClearMessage { export interface ICreationRequestMessage { type: 'html'; - content: string; + content: + | { type: RenderOutputType.Html; htmlContent: string } + | { type: RenderOutputType.Extension; output: IDisplayOutput; mimeType: string }; cellId: string; outputId: string; top: number; left: number; - requiredPreloads: IPreloadResource[]; + requiredPreloads: ReadonlyArray; initiallyHidden?: boolean; apiNamespace?: string | undefined; } @@ -200,7 +202,7 @@ export type AnyMessage = FromWebviewMessage | ToWebviewMessage; interface ICachedInset { outputId: string; cell: CodeCellViewModel; - preloads: ReadonlySet; + renderer?: INotebookRendererInfo; cachedCreation: ICreationRequestMessage; } @@ -224,11 +226,11 @@ export class BackLayerWebView extends Disposable { insetMapping: Map = new Map(); hiddenInsetMapping: Set = new Set(); reversedInsetMapping: Map = new Map(); - preloadsCache: Map = new Map(); localResourceRootsCache: URI[] | undefined = undefined; rendererRootsCache: URI[] = []; kernelRootsCache: URI[] = []; private readonly _onMessage = this._register(new Emitter()); + private readonly _preloadsCache = new Set(); public readonly onMessage: Event = this._onMessage.event; private _loaded!: Promise; private _initalized?: Promise; @@ -425,9 +427,17 @@ ${loaderJs} return; } - this.preloadsCache.clear(); + let renderers = new Set(); + for (const inset of this.insetMapping.values()) { + if (inset.renderer) { + renderers.add(inset.renderer); + } + } + + this._preloadsCache.clear(); + this.updateRendererPreloads(renderers); + for (const [output, inset] of this.insetMapping.entries()) { - this.updateRendererPreloads(inset.preloads); this._sendMessageToWebview({ ...inset.cachedCreation, initiallyHidden: this.hiddenInsetMapping.has(output) }); } })); @@ -622,19 +632,18 @@ ${loaderJs} }); } - async createInset(cell: CodeCellViewModel, output: IProcessedOutput, cellTop: number, offset: number, shadowContent: string, preloads: Set) { + async createInset(cell: CodeCellViewModel, content: IInsetRenderOutput, cellTop: number, offset: number) { if (this._disposed) { return; } - const requiredPreloads = await this.updateRendererPreloads(preloads); const initialTop = cellTop + offset; - if (this.insetMapping.has(output)) { - const outputCache = this.insetMapping.get(output); + if (this.insetMapping.has(content.source)) { + const outputCache = this.insetMapping.get(content.source); if (outputCache) { - this.hiddenInsetMapping.delete(output); + this.hiddenInsetMapping.delete(content.source); this._sendMessageToWebview({ type: 'showOutput', cellId: outputCache.cell.id, @@ -645,30 +654,49 @@ ${loaderJs} } } - const outputId = output.outputKind === CellOutputKind.Rich ? output.outputId : UUID.generateUuid(); - let apiNamespace: string | undefined; - if (output.outputKind === CellOutputKind.Rich && output.pickedMimeTypeIndex !== undefined) { - const pickedMimeTypeRenderer = output.orderedMimeTypes?.[output.pickedMimeTypeIndex]; - if (pickedMimeTypeRenderer?.rendererId) { - apiNamespace = this.notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId)?.id; - } + const messageBase = { + type: 'html', + cellId: cell.id, + top: initialTop, + left: 0, + requiredPreloads: [], + } as const; + + let message: ICreationRequestMessage; + let renderer: INotebookRendererInfo | undefined; + if (content.type === RenderOutputType.Extension) { + const output = content.source as ITransformedDisplayOutputDto; + renderer = content.renderer; + message = { + ...messageBase, + outputId: output.outputId, + apiNamespace: content.renderer.id, + requiredPreloads: await this.updateRendererPreloads([content.renderer]), + content: { + type: RenderOutputType.Extension, + mimeType: content.mimeType, + output: { + outputKind: CellOutputKind.Rich, + metadata: output.metadata, + data: output.data, + }, + }, + }; + } else { + message = { + ...messageBase, + outputId: UUID.generateUuid(), + content: { + type: content.type, + htmlContent: content.htmlContent, + } + }; } - const message: ICreationRequestMessage = { - type: 'html', - content: shadowContent, - cellId: cell.id, - apiNamespace, - outputId: outputId, - top: initialTop, - requiredPreloads, - left: 0 - }; - this._sendMessageToWebview(message); - this.insetMapping.set(output, { outputId: outputId, cell: cell, preloads, cachedCreation: message }); - this.hiddenInsetMapping.delete(output); - this.reversedInsetMapping.set(outputId, output); + this.insetMapping.set(content.source, { outputId: message.outputId, cell, renderer, cachedCreation: message }); + this.hiddenInsetMapping.delete(content.source); + this.reversedInsetMapping.set(message.outputId, content.source); } removeInset(output: IProcessedOutput) { @@ -774,9 +802,9 @@ ${loaderJs} }); preloads.forEach(e => { - if (!this.preloadsCache.has(e.toString())) { + if (!this._preloadsCache.has(e.toString())) { resources.push({ uri: e.toString() }); - this.preloadsCache.set(e.toString(), true); + this._preloadsCache.add(e.toString()); } }); @@ -788,7 +816,7 @@ ${loaderJs} this._updatePreloads(resources, 'kernel'); } - async updateRendererPreloads(preloads: ReadonlySet) { + async updateRendererPreloads(renderers: Iterable) { if (this._disposed) { return []; } @@ -798,28 +826,21 @@ ${loaderJs} const requiredPreloads: IPreloadResource[] = []; const resources: IPreloadResource[] = []; const extensionLocations: URI[] = []; - preloads.forEach(preload => { - const rendererInfo = this.notebookService.getRendererInfo(preload); + for (const rendererInfo of renderers) { + const preloads = [rendererInfo.entrypoint, ...rendererInfo.preloads] + .map(preload => asWebviewUri(this.workbenchEnvironmentService, this.id, preload)); + extensionLocations.push(rendererInfo.extensionLocation); - if (rendererInfo) { - const preloadResources = rendererInfo.preloads.map(preloadResource => { - if (this.environmentService.isExtensionDevelopment && (preloadResource.scheme === 'http' || preloadResource.scheme === 'https')) { - return preloadResource; - } - return asWebviewUri(this.workbenchEnvironmentService, this.id, preloadResource); - }); - extensionLocations.push(rendererInfo.extensionLocation); - preloadResources.forEach(e => { - const resource: IPreloadResource = { uri: e.toString() }; - requiredPreloads.push(resource); + preloads.forEach(e => { + const resource: IPreloadResource = { uri: e.toString() }; + requiredPreloads.push(resource); - if (!this.preloadsCache.has(e.toString())) { - resources.push(resource); - this.preloadsCache.set(e.toString(), true); - } - }); - } - }); + if (!this._preloadsCache.has(e.toString())) { + resources.push(resource); + this._preloadsCache.add(e.toString()); + } + }); + } if (!resources.length) { return requiredPreloads; @@ -855,7 +876,7 @@ ${loaderJs} } clearPreloadsCache() { - this.preloadsCache.clear(); + this._preloadsCache.clear(); } dispose() { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index ef7e8c202a1..03b24d3ca06 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -15,7 +15,7 @@ import { CellFocusMode, CodeCellRenderTemplate, INotebookEditor } from 'vs/workb import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; -import { CellOutputKind, IProcessedOutput, IRenderOutput, ITransformedDisplayOutputDto, BUILTIN_RENDERER_ID } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellOutputKind, IProcessedOutput, IRenderOutput, ITransformedDisplayOutputDto, BUILTIN_RENDERER_ID, RenderOutputType, outputHasDynamicHeight } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IDimension } from 'vs/editor/common/editorCommon'; @@ -322,7 +322,7 @@ export class CodeCell extends Disposable { const renderedOutput = this.outputElements.get(currOutput); if (renderedOutput) { - if (renderedOutput.renderResult.shadowContent) { + if (renderedOutput.renderResult.type !== RenderOutputType.None) { // Show inset in webview, or render output that isn't rendered this.renderOutput(currOutput, index, undefined); } else { @@ -408,9 +408,10 @@ export class CodeCell extends Disposable { } ); + // for contents for which we don't observe for dynamic height, update them manually this.viewCell.outputs.forEach((o, i) => { const renderedOutput = this.outputElements.get(o); - if (renderedOutput && !renderedOutput.renderResult.hasDynamicHeight && !renderedOutput.renderResult.shadowContent) { + if (renderedOutput && renderedOutput.renderResult.type === RenderOutputType.None && !renderedOutput.renderResult.hasDynamicHeight) { this.viewCell.updateOutputHeight(i, renderedOutput.element.clientHeight); } }); @@ -469,9 +470,12 @@ export class CodeCell extends Disposable { const innerContainer = DOM.$('.output-inner-container'); DOM.append(outputItemDiv, innerContainer); - if (pickedMimeTypeRenderer.isResolved) { - // html - result = this.notebookEditor.getOutputRenderer().render({ outputId: currOutput.outputId, outputKind: CellOutputKind.Rich, data: { 'text/html': pickedMimeTypeRenderer.output! } }, innerContainer, 'text/html'); + + if (pickedMimeTypeRenderer.rendererId !== BUILTIN_RENDERER_ID) { + const renderer = this.notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId); + result = renderer + ? { type: RenderOutputType.Extension, renderer, source: currOutput, mimeType: pickedMimeTypeRenderer.mimeType } + : this.notebookEditor.getOutputRenderer().render(currOutput, innerContainer, pickedMimeTypeRenderer.mimeType); } else { result = this.notebookEditor.getOutputRenderer().render(currOutput, innerContainer, pickedMimeTypeRenderer.mimeType); } @@ -496,18 +500,16 @@ export class CodeCell extends Disposable { this.templateData.outputContainer?.appendChild(outputItemDiv); } - if (result.shadowContent) { + if (result.type !== RenderOutputType.None) { this.viewCell.selfSizeMonitoring = true; - this.notebookEditor.createInset(this.viewCell, currOutput, result.shadowContent, this.viewCell.getOutputOffset(index)); + this.notebookEditor.createInset(this.viewCell, result, this.viewCell.getOutputOffset(index)); } else { DOM.addClass(outputItemDiv, 'foreground'); DOM.addClass(outputItemDiv, 'output-element'); outputItemDiv.style.position = 'absolute'; } - const hasDynamicHeight = result.hasDynamicHeight; - - if (hasDynamicHeight) { + if (outputHasDynamicHeight(result)) { this.viewCell.selfSizeMonitoring = true; const clientHeight = outputItemDiv.clientHeight; @@ -535,18 +537,12 @@ export class CodeCell extends Disposable { elementSizeObserver.startObserving(); this.outputResizeListeners.get(currOutput)!.add(elementSizeObserver); this.viewCell.updateOutputHeight(index, clientHeight); - } else { - if (result.shadowContent) { - // webview - // noop - } else { - // static output - const clientHeight = Math.ceil(outputItemDiv.clientHeight); - this.viewCell.updateOutputHeight(index, clientHeight); + } else if (result.type !== RenderOutputType.None) { // no-op if it's a webview + const clientHeight = Math.ceil(outputItemDiv.clientHeight); + this.viewCell.updateOutputHeight(index, clientHeight); - const top = this.viewCell.getOutputOffsetInContainer(index); - outputItemDiv.style.top = `${top}px`; - } + const top = this.viewCell.getOutputOffsetInContainer(index); + outputItemDiv.style.top = `${top}px`; } } @@ -605,17 +601,6 @@ export class CodeCell extends Disposable { } output.pickedMimeTypeIndex = pick; - - if (!output.orderedMimeTypes![pick].isResolved && output.orderedMimeTypes![pick].rendererId !== BUILTIN_RENDERER_ID) { - // since it's not build in renderer and not resolved yet - // let's see if we can activate the extension and then render - // await this.notebookService.transformSpliceOutputs(this.notebookEditor.textModel!, [[0, 0, output]]) - const outputRet = await this.notebookService.transformSingleOutput(this.notebookEditor.textModel!, output, output.orderedMimeTypes![pick].rendererId!, output.orderedMimeTypes![pick].mimeType); - if (outputRet) { - output.orderedMimeTypes![pick] = outputRet; - } - } - this.renderOutput(output, index, nextElement); this.relayoutCell(); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 622c1104e97..9caa12e52b7 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -6,6 +6,7 @@ import type { Event } from 'vs/base/common/event'; import type { IDisposable } from 'vs/base/common/lifecycle'; import { ToWebviewMessage } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView'; +import { RenderOutputType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; // !! IMPORTANT !! everything must be in-line within the webviewPreloads // function. Imports are not allowed. This is stringifies and injected into @@ -373,24 +374,21 @@ function webviewPreloads() { addMouseoverListeners(outputNode, outputId); const content = data.content; - outputNode.innerHTML = content; - cellOutputContainer.appendChild(outputNode); - - let pureData: { mimeType: string, output: unknown } | undefined; - const outputScript = cellOutputContainer.querySelector('script.vscode-pure-data'); - if (outputScript) { - try { pureData = JSON.parse(outputScript.innerHTML); } catch { } + if (content.type === RenderOutputType.Html) { + outputNode.innerHTML = content.htmlContent; + cellOutputContainer.appendChild(outputNode); + domEval(outputNode); + } else { + onDidCreateOutput.fire([data.apiNamespace, { + element: outputNode, + output: content.output, + mimeType: content.mimeType, + outputId + }]); + cellOutputContainer.appendChild(outputNode); } - // eval - domEval(outputNode); resizeObserve(outputNode, outputId); - onDidCreateOutput.fire([data.apiNamespace, { - element: outputNode, - output: pureData?.output, - mimeType: pureData?.mimeType, - outputId - }]); vscode.postMessage({ __vscode_notebook_message: true, diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts index b04c3e18ed5..3c26621a682 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts @@ -170,10 +170,6 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD return this._notebook; } - get renderers() { - return this._notebook!.renderers; - } - get handle() { return this._notebook.handle; } diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index d48a7968a42..920d34f6f70 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -136,7 +136,6 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel cells: NotebookCellTextModel[]; languages: string[] = []; metadata: NotebookDocumentMetadata = notebookDocumentMetadataDefaults; - renderers = new Set(); private _isUntitled: boolean | undefined = undefined; private _versionId = 0; @@ -369,12 +368,6 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel } } - updateRenderers(renderers: string[]) { - renderers.forEach(render => { - this.renderers.add(render); - }); - } - insertTemplateCell(cell: NotebookCellTextModel) { if (this.cells.length > 0 || this._isUntitled !== undefined) { return; diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 319c8fd9130..68b34d4a5d3 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -114,11 +114,12 @@ export interface INotebookMimeTypeSelector { export interface INotebookRendererInfo { id: string; displayName: string; + entrypoint: URI; + preloads: ReadonlyArray; + extensionLocation: URI; extensionId: ExtensionIdentifier; - extensionLocation: URI, - preloads: URI[], - render(uri: URI, request: IOutputRenderRequest): Promise | undefined>; - render2(uri: URI, request: IOutputRenderRequest): Promise | undefined>; + + matches(mimeType: string): boolean; } export interface INotebookKernelInfo { @@ -190,9 +191,7 @@ export enum MimeTypeRendererResolver { export interface IOrderedMimeType { mimeType: string; - isResolved: boolean; - rendererId?: string; - output?: string; + rendererId: string; } export interface ITransformedDisplayOutputDto { @@ -280,17 +279,41 @@ export interface INotebookTextModel { readonly versionId: number; languages: string[]; cells: ICell[]; - renderers: Set; onDidChangeCells?: Event<{ synchronous: boolean, splices: NotebookCellTextModelSplice[] }>; onDidChangeContent: Event; onWillDispose(listener: () => void): IDisposable; } -export interface IRenderOutput { - shadowContent?: string; +export const enum RenderOutputType { + None, + Html, + Extension +} + +export interface IRenderNoOutput { + type: RenderOutputType.None; hasDynamicHeight: boolean; } +export interface IRenderPlainHtmlOutput { + type: RenderOutputType.Html; + source: IProcessedOutput; + htmlContent: string; + hasDynamicHeight: boolean; +} + +export interface IRenderOutputViaExtension { + type: RenderOutputType.Extension; + source: IProcessedOutput; + mimeType: string; + renderer: INotebookRendererInfo; +} + +export type IInsetRenderOutput = IRenderPlainHtmlOutput | IRenderOutputViaExtension; +export type IRenderOutput = IRenderNoOutput | IInsetRenderOutput; + +export const outputHasDynamicHeight = (o: IRenderOutput) => o.type === RenderOutputType.Extension || o.hasDynamicHeight; + export type NotebookCellTextModelSplice = [ number /* start */, number, diff --git a/src/vs/workbench/contrib/notebook/common/notebookOutputRenderer.ts b/src/vs/workbench/contrib/notebook/common/notebookOutputRenderer.ts index b22c64145eb..4ac836f1f9d 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookOutputRenderer.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookOutputRenderer.ts @@ -4,31 +4,42 @@ *--------------------------------------------------------------------------------------------*/ import * as glob from 'vs/base/common/glob'; +import { joinPath } from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { INotebookRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -export class NotebookOutputRendererInfo { +export class NotebookOutputRendererInfo implements INotebookRendererInfo { readonly id: string; + readonly entrypoint: URI; readonly displayName: string; - readonly mimeTypes: readonly string[]; - readonly mimeTypeGlobs: glob.ParsedPattern[]; + readonly extensionLocation: URI; + readonly extensionId: ExtensionIdentifier; + // todo: re-add preloads in pure renderer API + readonly preloads: ReadonlyArray = []; + + private readonly mimeTypes: readonly string[]; + private readonly mimeTypeGlobs: glob.ParsedPattern[]; constructor(descriptor: { readonly id: string; readonly displayName: string; + readonly entrypoint: string; readonly mimeTypes: readonly string[]; + readonly extension: IExtensionDescription; }) { this.id = descriptor.id; + this.extensionId = descriptor.extension.identifier; + this.extensionLocation = descriptor.extension.extensionLocation; + this.entrypoint = joinPath(this.extensionLocation, descriptor.entrypoint); this.displayName = descriptor.displayName; this.mimeTypes = descriptor.mimeTypes; this.mimeTypeGlobs = this.mimeTypes.map(pattern => glob.parse(pattern)); } matches(mimeType: string) { - const matched = this.mimeTypeGlobs.find(pattern => pattern(mimeType)); - if (matched) { - return true; - } - - return this.mimeTypes.find(pattern => pattern === mimeType); + return this.mimeTypeGlobs.some(pattern => pattern(mimeType)) + || this.mimeTypes.some(pattern => pattern === mimeType); } } diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index 87fad7fb364..1eb32840394 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -10,7 +10,7 @@ import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.pr import { Event } from 'vs/base/common/event'; import { INotebookTextModel, INotebookRendererInfo, INotebookKernelInfo, INotebookKernelInfoDto, - IEditor, ICellEditOperation, NotebookCellOutputsSplice, IOrderedMimeType, IProcessedOutput, INotebookKernelProvider, INotebookKernelInfo2 + IEditor, ICellEditOperation, NotebookCellOutputsSplice, INotebookKernelProvider, INotebookKernelInfo2 } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -52,11 +52,8 @@ export interface INotebookService { onDidChangeNotebookActiveKernel: Event<{ uri: URI, providerHandle: number | undefined, kernelId: string | undefined }>; registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController): void; unregisterNotebookProvider(viewType: string): void; - registerNotebookRenderer(id: string, renderer: INotebookRendererInfo): void; - unregisterNotebookRenderer(id: string): void; - transformEditsOutputs(textModel: NotebookTextModel, edits: ICellEditOperation[]): Promise; - transformSpliceOutputs(textModel: NotebookTextModel, splices: NotebookCellOutputsSplice[]): Promise; - transformSingleOutput(textModel: NotebookTextModel, output: IProcessedOutput, rendererId: string, mimeType: string): Promise; + transformEditsOutputs(textModel: NotebookTextModel, edits: ICellEditOperation[]): void; + transformSpliceOutputs(textModel: NotebookTextModel, splices: NotebookCellOutputsSplice[]): void; registerNotebookKernel(kernel: INotebookKernelInfo): void; unregisterNotebookKernel(id: string): void; registerNotebookKernelProvider(provider: INotebookKernelProvider): IDisposable; diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index 463c36e60c3..98d72f689d3 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -18,7 +18,7 @@ import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/v import { CellViewModel, IModelDecorationsChangeAccessor, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellKind, CellUri, INotebookEditorModel, IProcessedOutput, NotebookCellMetadata, INotebookKernelInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellUri, INotebookEditorModel, IProcessedOutput, NotebookCellMetadata, INotebookKernelInfo, IInsetRenderOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { ICompositeCodeEditor, IEditor } from 'vs/editor/common/editorCommon'; import { NotImplementedError } from 'vs/base/common/errors'; @@ -259,7 +259,7 @@ export class TestNotebookEditor implements INotebookEditor { // throw new Error('Method not implemented.'); return; } - createInset(cell: CellViewModel, output: IProcessedOutput, shadowContent: string, offset: number): Promise { + createInset(cell: CellViewModel, output: IInsetRenderOutput, offset: number): Promise { return Promise.resolve(); } removeInset(output: IProcessedOutput): void { From 64d126cafbac5e94519f635602e03a20b1f28db1 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 19 Aug 2020 17:06:55 -0700 Subject: [PATCH 364/736] Add new TSS_REMOTE_DEBUG flags that are only active in remote cases Fixes https://github.com/microsoft/vscode-remote-release/issues/3546 --- .../src/tsServer/serverProcess.electron.ts | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/extensions/typescript-language-features/src/tsServer/serverProcess.electron.ts b/extensions/typescript-language-features/src/tsServer/serverProcess.electron.ts index f9d70d858e0..96b0c6b411e 100644 --- a/extensions/typescript-language-features/src/tsServer/serverProcess.electron.ts +++ b/extensions/typescript-language-features/src/tsServer/serverProcess.electron.ts @@ -173,12 +173,19 @@ export class ChildServerProcess extends Disposable implements TsServerProcess { } private static getExecArgv(kind: TsServerProcessKind, configuration: TypeScriptServiceConfiguration): string[] { + const args: string[] = []; + const debugPort = this.getDebugPort(kind); - const inspectFlag = process.env['TSS_DEBUG_BRK'] ? '--inspect-brk' : '--inspect'; - return [ - ...(debugPort ? [`${inspectFlag}=${debugPort}`] : []), - ...(configuration.maxTsServerMemory ? [`--max-old-space-size=${configuration.maxTsServerMemory}`] : []) - ]; + if (debugPort) { + const inspectFlag = ChildServerProcess.getTssDebugBrk() ? '--inspect-brk' : '--inspect'; + args.push(`${inspectFlag}=${debugPort}`); + } + + if (configuration.maxTsServerMemory) { + args.push(`--max-old-space-size=${configuration.maxTsServerMemory}`); + } + + return args; } private static getDebugPort(kind: TsServerProcessKind): number | undefined { @@ -186,7 +193,7 @@ export class ChildServerProcess extends Disposable implements TsServerProcess { // We typically only want to debug the main semantic server return undefined; } - const value = process.env['TSS_DEBUG_BRK'] || process.env['TSS_DEBUG']; + const value = ChildServerProcess.getTssDebugBrk() || ChildServerProcess.getTssDebug(); if (value) { const port = parseInt(value); if (!isNaN(port)) { @@ -196,6 +203,14 @@ export class ChildServerProcess extends Disposable implements TsServerProcess { return undefined; } + private static getTssDebug(): string | undefined { + return process.env[vscode.env.remoteName ? 'TSS_REMOTE_DEBUG' : 'TSS_DEBUG']; + } + + private static getTssDebugBrk(): string | undefined { + return process.env[vscode.env.remoteName ? 'TSS_REMOTE_DEBUG_BRK' : 'TSS_DEBUG_BRK']; + } + private constructor( private readonly _process: child_process.ChildProcess, ) { From ad4e97584c16996b1c3342aeffbbce6ea3ef070d Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 19 Aug 2020 18:18:39 -0700 Subject: [PATCH 365/736] Force delete button to right Fix #104082 --- .../notebook/browser/contrib/coreActions.ts | 24 +++++++++++++++---- .../notebook/browser/media/notebook.css | 2 +- .../browser/view/renderers/cellRenderer.ts | 18 ++++++++++---- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 63f2adeb309..90000b71cf2 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -87,8 +87,7 @@ const enum CellToolbarOrder { EditCell, SplitCell, SaveCell, - ClearCellOutput, - DeleteCell + ClearCellOutput } const enum CellOverflowToolbarGroups { @@ -261,6 +260,23 @@ export class CancelCellAction extends MenuItemAction { } } +export class DeleteCellAction extends MenuItemAction { + constructor( + @IContextKeyService contextKeyService: IContextKeyService, + @ICommandService commandService: ICommandService + ) { + super( + { + id: DELETE_CELL_COMMAND_ID, + title: localize('notebookActions.deleteCell', "Delete Cell"), + icon: { id: 'codicon/trash' } + }, + undefined, + { shouldForwardArgs: true }, + contextKeyService, + commandService); + } +} registerAction2(class extends NotebookCellAction { constructor() { @@ -775,9 +791,7 @@ registerAction2(class extends NotebookCellAction { title: localize('notebookActions.deleteCell', "Delete Cell"), menu: { id: MenuId.NotebookCellTitle, - order: CellToolbarOrder.DeleteCell, - when: NOTEBOOK_EDITOR_EDITABLE, - group: CELL_TITLE_CELL_GROUP_ID + when: NOTEBOOK_EDITOR_EDITABLE }, keybinding: { primary: KeyCode.Delete, diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index ffd4ca82fa9..c4bf75602a0 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -291,7 +291,7 @@ .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-title-toolbar { visibility: hidden; - display: inline-block; + display: inline-flex; position: absolute; height: 26px; right: 44px; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 40011006554..76a93795aa8 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -36,7 +36,7 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_TOP_MARGIN, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CancelCellAction, ExecuteCellAction, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; +import { CancelCellAction, DeleteCellAction, ExecuteCellAction, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; import { BaseCellRenderTemplate, CellEditState, CodeCellRenderTemplate, EXPAND_CELL_CONTENT_COMMAND_ID, ICellViewModel, INotebookEditor, isCodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys'; import { CellMenus } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellMenus'; @@ -379,7 +379,12 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR const container = DOM.append(rootContainer, DOM.$('.cell-inner-container')); const disposables = new DisposableStore(); const contextKeyService = disposables.add(this.contextKeyServiceProvider(container)); - const toolbar = disposables.add(this.createToolbar(container, 'cell-title-toolbar')); + + const titleToolbarContainer = DOM.append(container, $('.cell-title-toolbar')); + const toolbar = disposables.add(this.createToolbar(titleToolbarContainer)); + const deleteToolbar = disposables.add(this.createToolbar(titleToolbarContainer, 'cell-delete-toolbar')); + deleteToolbar.setActions([this.instantiationService.createInstance(DeleteCellAction)]); + const focusIndicatorLeft = DOM.append(container, DOM.$('.cell-focus-indicator.cell-focus-indicator-side.cell-focus-indicator-left')); const codeInnerContent = DOM.append(container, $('.cell.code')); @@ -644,14 +649,17 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende const contextKeyService = disposables.add(this.contextKeyServiceProvider(container)); DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-top')); - const toolbar = disposables.add(this.createToolbar(container, 'cell-title-toolbar')); + const titleToolbarContainer = DOM.append(container, $('.cell-title-toolbar')); + const toolbar = disposables.add(this.createToolbar(titleToolbarContainer)); + const deleteToolbar = disposables.add(this.createToolbar(titleToolbarContainer, 'cell-delete-toolbar')); + deleteToolbar.setActions([this.instantiationService.createInstance(DeleteCellAction)]); + const focusIndicator = DOM.append(container, DOM.$('.cell-focus-indicator.cell-focus-indicator-side.cell-focus-indicator-left')); const dragHandle = DOM.append(container, DOM.$('.cell-drag-handle')); const cellContainer = DOM.append(container, $('.cell.code')); const runButtonContainer = DOM.append(cellContainer, $('.run-button-container')); - const runToolbar = this.createToolbar(runButtonContainer); - disposables.add(runToolbar); + const runToolbar = disposables.add(this.createToolbar(runButtonContainer)); const executionOrderLabel = DOM.append(runButtonContainer, $('div.execution-count-label')); From 8e872f41d4ba7807e58938bbf861ca6a22d534aa Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 19 Aug 2020 19:23:02 -0700 Subject: [PATCH 366/736] Move execution order label Fix #104680 --- .../notebook/browser/media/notebook.css | 21 +++++++++---------- .../browser/view/renderers/cellRenderer.ts | 2 +- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index c4bf75602a0..b8d061f818b 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -61,6 +61,10 @@ z-index: 1000; } +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image .execution-count-label { + display: none; +} + .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image .cell-editor-container > div { padding: 12px 16px; } @@ -401,24 +405,19 @@ visibility: visible; } -.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container .execution-count-label { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .execution-count-label { position: absolute; - top: -2px; font-size: 10px; font-family: var(--monaco-monospace-font); - visibility: visible; white-space: pre; - width: 100%; - text-align: center; - padding-right: 8px; box-sizing: border-box; opacity: .6; -} -.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .cell .run-button-container .execution-count-label, -.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-output-hover .cell .run-button-container .execution-count-label, -.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused .cell .run-button-container .execution-count-label { - visibility: hidden; + /* Sizing hacks */ + left: 26px; + width: 35px; + bottom: 0px; + text-align: center; } .monaco-workbench .notebookOverlay .cell .cell-editor-part { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 76a93795aa8..f2195abb858 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -661,7 +661,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende const runButtonContainer = DOM.append(cellContainer, $('.run-button-container')); const runToolbar = disposables.add(this.createToolbar(runButtonContainer)); - const executionOrderLabel = DOM.append(runButtonContainer, $('div.execution-count-label')); + const executionOrderLabel = DOM.append(cellContainer, $('div.execution-count-label')); // create a special context key service that set the inCompositeEditor-contextkey const editorContextKeyService = disposables.add(this.contextKeyServiceProvider(container)); From 1e77267623a9072883f0a69957805e66bb953924 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 20 Aug 2020 09:52:47 +0200 Subject: [PATCH 367/736] format on save to only format modifications --- src/vs/editor/contrib/format/format.ts | 2 + src/vs/editor/contrib/format/formatActions.ts | 4 +- .../codeEditor/browser/saveParticipants.ts | 31 +++++-- .../files/browser/files.contribution.ts | 14 ++- .../format/browser/format.contribution.ts | 2 +- .../{formatChanges.ts => formatModified.ts} | 89 +++++++++---------- .../contrib/scm/browser/dirtydiffDecorator.ts | 30 ++++--- 7 files changed, 98 insertions(+), 74 deletions(-) rename src/vs/workbench/contrib/format/browser/{formatChanges.ts => formatModified.ts} (51%) diff --git a/src/vs/editor/contrib/format/format.ts b/src/vs/editor/contrib/format/format.ts index 493a6ee340d..df4b5cdcd4f 100644 --- a/src/vs/editor/contrib/format/format.ts +++ b/src/vs/editor/contrib/format/format.ts @@ -125,6 +125,7 @@ export async function formatDocumentRangesWithSelectedProvider( editorOrModel: ITextModel | IActiveCodeEditor, rangeOrRanges: Range | Range[], mode: FormattingMode, + progress: IProgress, token: CancellationToken ): Promise { @@ -133,6 +134,7 @@ export async function formatDocumentRangesWithSelectedProvider( const provider = DocumentRangeFormattingEditProviderRegistry.ordered(model); const selected = await FormattingConflicts.select(provider, model, mode); if (selected) { + progress.report(selected); await instaService.invokeFunction(formatDocumentRangesWithProvider, selected, editorOrModel, rangeOrRanges, token); } } diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index 5c7ab599ce7..350aeef860e 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -202,7 +202,7 @@ class FormatOnPaste implements IEditorContribution { if (this.editor.getSelections().length > 1) { return; } - this._instantiationService.invokeFunction(formatDocumentRangesWithSelectedProvider, this.editor, range, FormattingMode.Silent, CancellationToken.None).catch(onUnexpectedError); + this._instantiationService.invokeFunction(formatDocumentRangesWithSelectedProvider, this.editor, range, FormattingMode.Silent, Progress.None, CancellationToken.None).catch(onUnexpectedError); } } @@ -274,7 +274,7 @@ class FormatSelectionAction extends EditorAction { const progressService = accessor.get(IEditorProgressService); await progressService.showWhile( - instaService.invokeFunction(formatDocumentRangesWithSelectedProvider, editor, range, FormattingMode.Explicit, CancellationToken.None), + instaService.invokeFunction(formatDocumentRangesWithSelectedProvider, editor, range, FormattingMode.Explicit, Progress.None, CancellationToken.None), 250 ); } diff --git a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts index 473166ff599..61c5b748882 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts @@ -5,7 +5,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import * as strings from 'vs/base/common/strings'; -import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IActiveCodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { trimTrailingWhitespace } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand'; import { EditOperation } from 'vs/editor/common/core/editOperation'; @@ -13,11 +13,11 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ITextModel } from 'vs/editor/common/model'; -import { CodeActionTriggerType, DocumentFormattingEditProvider, CodeActionProvider } from 'vs/editor/common/modes'; +import { CodeActionTriggerType, CodeActionProvider } from 'vs/editor/common/modes'; import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction'; import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands'; import { CodeActionKind } from 'vs/editor/contrib/codeAction/types'; -import { formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format'; +import { formatDocumentRangesWithSelectedProvider, formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format'; import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -29,6 +29,8 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContribution, Extensions as WorkbenchContributionsExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { getModifiedRanges } from 'vs/workbench/contrib/format/browser/formatModified'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export class TrimWhitespaceParticipant implements ITextFileSaveParticipant { @@ -221,15 +223,14 @@ class FormatOnSaveParticipant implements ITextFileSaveParticipant { if (!model.textEditorModel) { return; } + if (env.reason === SaveReason.AUTO) { + return undefined; + } const textEditorModel = model.textEditorModel; const overrides = { overrideIdentifier: textEditorModel.getLanguageIdentifier().language, resource: textEditorModel.uri }; - if (env.reason === SaveReason.AUTO || !this.configurationService.getValue('editor.formatOnSave', overrides)) { - return undefined; - } - - const nestedProgress = new Progress(provider => { + const nestedProgress = new Progress<{ displayName?: string, extensionId?: ExtensionIdentifier }>(provider => { progress.report({ message: localize( 'formatting', @@ -239,7 +240,19 @@ class FormatOnSaveParticipant implements ITextFileSaveParticipant { }); }); const editorOrModel = findEditor(textEditorModel, this.codeEditorService) || textEditorModel; - await this.instantiationService.invokeFunction(formatDocumentWithSelectedProvider, editorOrModel, FormattingMode.Silent, nestedProgress, token); + const config = this.configurationService.getValue('editor.formatOnSave', overrides); + + if (config === true || config === 'file') { + // format the whole file + await this.instantiationService.invokeFunction(formatDocumentWithSelectedProvider, editorOrModel, FormattingMode.Silent, nestedProgress, token); + + } else if (config === 'modifications') { + // format modifications + const ranges = await this.instantiationService.invokeFunction(getModifiedRanges, isCodeEditor(editorOrModel) ? editorOrModel.getModel() : editorOrModel); + if (ranges) { + await this.instantiationService.invokeFunction(formatDocumentRangesWithSelectedProvider, editorOrModel, ranges, FormattingMode.Silent, nestedProgress, token); + } + } } } diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index 71f86a19285..c4ee45a0362 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -360,8 +360,18 @@ configurationRegistry.registerConfiguration({ ...editorConfigurationBaseNode, properties: { 'editor.formatOnSave': { - 'type': 'boolean', - 'default': false, + 'type': 'string', + 'default': 'off', + 'enum': [ + 'off', + 'file', + 'modifications' + ], + 'enumDescriptions': [ + nls.localize('off', "Disable format on save."), + nls.localize('everything', "Format the whole file on save."), + nls.localize('modification', "Only format modifications (requires source control)."), + ], 'description': nls.localize('formatOnSave', "Format a file on save. A formatter must be available, the file must not be saved after delay, and the editor must not be shutting down."), scope: ConfigurationScope.LANGUAGE_OVERRIDABLE, } diff --git a/src/vs/workbench/contrib/format/browser/format.contribution.ts b/src/vs/workbench/contrib/format/browser/format.contribution.ts index 050ca89f529..a91827cdc47 100644 --- a/src/vs/workbench/contrib/format/browser/format.contribution.ts +++ b/src/vs/workbench/contrib/format/browser/format.contribution.ts @@ -5,4 +5,4 @@ import './formatActionsMultiple'; import './formatActionsNone'; -import './formatChanges'; +import './formatModified'; diff --git a/src/vs/workbench/contrib/format/browser/formatChanges.ts b/src/vs/workbench/contrib/format/browser/formatModified.ts similarity index 51% rename from src/vs/workbench/contrib/format/browser/formatChanges.ts rename to src/vs/workbench/contrib/format/browser/formatModified.ts index fd745b3bab3..e1ae53814d7 100644 --- a/src/vs/workbench/contrib/format/browser/formatChanges.ts +++ b/src/vs/workbench/contrib/format/browser/formatModified.ts @@ -5,84 +5,79 @@ import { isNonEmptyArray } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { isEqualOrParent } from 'vs/base/common/resources'; -import { URI } from 'vs/base/common/uri'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, registerEditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { Range } from 'vs/editor/common/core/range'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { ITextModel } from 'vs/editor/common/model'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { formatDocumentRangesWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format'; import * as nls from 'vs/nls'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ISCMProvider, ISCMService } from 'vs/workbench/contrib/scm/common/scm'; +import { Progress } from 'vs/platform/progress/common/progress'; +import { getOriginalResource } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; +import { ISCMService } from 'vs/workbench/contrib/scm/common/scm'; -registerEditorAction(class FormatChangedLinesAction extends EditorAction { +registerEditorAction(class FormatModifiedAction extends EditorAction { constructor() { super({ id: 'editor.action.formatChanges', - label: nls.localize('formatChanges', "Format Changed Lines"), - alias: 'Format Document...', + label: nls.localize('formatChanges', "Format Modified Lines"), + alias: 'Format Modified Lines', precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasDocumentSelectionFormattingProvider), }); } async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { - const scmService = accessor.get(ISCMService); - const workerService = accessor.get(IEditorWorkerService); - const modelService = accessor.get(ITextModelService); const instaService = accessor.get(IInstantiationService); if (!editor.hasModel()) { return; } - const modified = editor.getModel().uri; - const provider = this._getBestProvider(scmService, modified); - if (!provider) { - return; - } - - const original = await provider.getOriginalResource(modified); - if (!original) { - return; - } - - const ranges: Range[] = []; - const ref = await modelService.createModelReference(original); - try { - if (workerService.canComputeDirtyDiff(original, modified)) { - const changes = await workerService.computeDirtyDiff(original, modified, true); - if (isNonEmptyArray(changes)) { - for (let change of changes) { - ranges.push(editor.getModel().validateRange(new Range( - change.modifiedStartLineNumber, 1, - change.modifiedEndLineNumber || change.modifiedStartLineNumber /*endLineNumber is 0 when things got deleted*/, Number.MAX_SAFE_INTEGER) - )); - } - } - } - } finally { - ref.dispose(); - } - - if (ranges.length > 0) { + const ranges = await instaService.invokeFunction(getModifiedRanges, editor.getModel()); + if (isNonEmptyArray(ranges)) { return instaService.invokeFunction( formatDocumentRangesWithSelectedProvider, editor, ranges, - FormattingMode.Explicit, CancellationToken.None + FormattingMode.Explicit, Progress.None, CancellationToken.None ); } } +}); - private _getBestProvider(scmService: ISCMService, uri: URI): ISCMProvider | undefined { - for (let repo of scmService.repositories) { - if (repo.provider.rootUri && isEqualOrParent(uri, repo.provider.rootUri)) { - return repo.provider; - } - } + +export async function getModifiedRanges(accessor: ServicesAccessor, modified: ITextModel): Promise { + const scmService = accessor.get(ISCMService); + const workerService = accessor.get(IEditorWorkerService); + const modelService = accessor.get(ITextModelService); + + const original = await getOriginalResource(scmService, modified.uri); + if (!original) { return undefined; } -}); + + const ranges: Range[] = []; + const ref = await modelService.createModelReference(original); + try { + if (!workerService.canComputeDirtyDiff(original, modified.uri)) { + return undefined; + } + const changes = await workerService.computeDirtyDiff(original, modified.uri, true); + if (!isNonEmptyArray(changes)) { + return undefined; + } + for (let change of changes) { + ranges.push(modified.validateRange(new Range( + change.modifiedStartLineNumber, 1, + change.modifiedEndLineNumber || change.modifiedStartLineNumber /*endLineNumber is 0 when things got deleted*/, Number.MAX_SAFE_INTEGER) + )); + } + } finally { + ref.dispose(); + } + + return ranges; +} diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index 450448112bb..a4440db8e93 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -999,6 +999,22 @@ function createProviderComparer(uri: URI): (a: ISCMProvider, b: ISCMProvider) => }; } +export async function getOriginalResource(scmService: ISCMService, uri: URI): Promise { + const providers = scmService.repositories.map(r => r.provider); + const rootedProviders = providers.filter(p => !!p.rootUri); + + rootedProviders.sort(createProviderComparer(uri)); + + const result = await first(rootedProviders.map(p => () => p.getOriginalResource(uri))); + + if (result) { + return result; + } + + const nonRootedProviders = providers.filter(p => !p.rootUri); + return first(nonRootedProviders.map(p => () => p.getOriginalResource(uri))); +} + export class DirtyDiffModel extends Disposable { private _originalModel: IResolvedTextFileEditorModel | null = null; @@ -1155,19 +1171,7 @@ export class DirtyDiffModel extends Disposable { } const uri = this._model.resource; - const providers = this.scmService.repositories.map(r => r.provider); - const rootedProviders = providers.filter(p => !!p.rootUri); - - rootedProviders.sort(createProviderComparer(uri)); - - const result = await first(rootedProviders.map(p => () => p.getOriginalResource(uri))); - - if (result) { - return result; - } - - const nonRootedProviders = providers.filter(p => !p.rootUri); - return first(nonRootedProviders.map(p => () => p.getOriginalResource(uri))); + return getOriginalResource(this.scmService, uri); } findNextClosestChange(lineNumber: number, inclusive = true): number { From 8199fcd2a2ed51d7eed6db6fa5ac5398771659bf Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 20 Aug 2020 10:03:39 +0200 Subject: [PATCH 368/736] tweak wording --- src/vs/workbench/contrib/files/browser/files.contribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index c4ee45a0362..0fdfb530004 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -369,8 +369,8 @@ configurationRegistry.registerConfiguration({ ], 'enumDescriptions': [ nls.localize('off', "Disable format on save."), - nls.localize('everything', "Format the whole file on save."), - nls.localize('modification', "Only format modifications (requires source control)."), + nls.localize('everything', "Format the whole."), + nls.localize('modification', "Format modifications (requires source control)."), ], 'description': nls.localize('formatOnSave', "Format a file on save. A formatter must be available, the file must not be saved after delay, and the editor must not be shutting down."), scope: ConfigurationScope.LANGUAGE_OVERRIDABLE, From 780fb8c8e8e31824712494b3c10ad253d5e23051 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 20 Aug 2020 10:42:51 +0200 Subject: [PATCH 369/736] fix https://github.com/microsoft/vscode/issues/104454 --- src/vs/workbench/browser/parts/editor/rangeDecorations.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/rangeDecorations.ts b/src/vs/workbench/browser/parts/editor/rangeDecorations.ts index 5f3a4cf23c5..d62cc3b59d5 100644 --- a/src/vs/workbench/browser/parts/editor/rangeDecorations.ts +++ b/src/vs/workbench/browser/parts/editor/rangeDecorations.ts @@ -10,7 +10,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IRange } from 'vs/editor/common/core/range'; import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, isCodeEditor, isCompositeEditor } from 'vs/editor/browser/editorBrowser'; import { TrackedRangeStickiness, IModelDecorationsChangeAccessor } from 'vs/editor/common/model'; export interface IRangeHighlightDecoration { @@ -44,9 +44,11 @@ export class RangeHighlightDecorations extends Disposable { } highlightRange(range: IRangeHighlightDecoration, editor?: any) { - editor = editor ? editor : this.getEditor(range); + editor = editor ?? this.getEditor(range); if (isCodeEditor(editor)) { this.doHighlightRange(editor, range); + } else if (isCompositeEditor(editor) && isCodeEditor(editor.activeCodeEditor)) { + this.doHighlightRange(editor.activeCodeEditor, range); } } From 79e9ee33c98a12bdb0f9911ed1d5b954dbb6e48c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 20 Aug 2020 10:46:56 +0200 Subject: [PATCH 370/736] rename and pass environment service --- .../parts/activitybar/activitybarActions.ts | 4 ++-- .../browser/authenticationService.ts | 23 ++++++++++--------- .../services/userData/browser/userDataInit.ts | 4 ++-- .../browser/userDataSyncWorkbenchService.ts | 4 ++-- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index ea841289048..241d3bced2d 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -28,7 +28,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { Codicon } from 'vs/base/common/codicons'; import { isMacintosh } from 'vs/base/common/platform'; -import { getAuthenticationSession, IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; +import { getCurrentAuthenticationSessionInfo, IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; import { AuthenticationSession } from 'vs/editor/common/modes'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; @@ -180,7 +180,7 @@ export class AccountsActionViewItem extends ActivityActionViewItem { const result = await Promise.all(allSessions); let menus: IAction[] = []; - const authenticationSession = this.environmentService.options?.credentialsProvider ? await getAuthenticationSession(this.environmentService.options?.credentialsProvider, this.productService) : undefined; + const authenticationSession = this.environmentService.options?.credentialsProvider ? await getCurrentAuthenticationSessionInfo(this.environmentService, this.productService) : undefined; result.forEach(sessionInfo => { const providerDisplayName = this.authenticationService.getLabel(sessionInfo.providerId); Object.keys(sessionInfo.sessions).forEach(accountName => { diff --git a/src/vs/workbench/services/authentication/browser/authenticationService.ts b/src/vs/workbench/services/authentication/browser/authenticationService.ts index 80de127f448..8346c283c32 100644 --- a/src/vs/workbench/services/authentication/browser/authenticationService.ts +++ b/src/vs/workbench/services/authentication/browser/authenticationService.ts @@ -16,22 +16,23 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { ICredentialsProvider } from 'vs/platform/credentials/common/credentials'; import { IProductService } from 'vs/platform/product/common/productService'; import { isString } from 'vs/base/common/types'; export function getAuthenticationProviderActivationEvent(id: string): string { return `onAuthenticationRequest:${id}`; } -export async function getAuthenticationSession(credentialsProvider: ICredentialsProvider, productService: IProductService): Promise<{ id: string, accessToken: string, providerId: string } | undefined> { - const authenticationSessionValue = await credentialsProvider.getPassword(`${productService.urlProtocol}.login`, 'account'); - if (authenticationSessionValue) { - const authenticationSession: { id: string, accessToken: string, providerId: string } = JSON.parse(authenticationSessionValue); - if (authenticationSession - && isString(authenticationSession.id) - && isString(authenticationSession.accessToken) - && isString(authenticationSession.providerId) - ) { - return authenticationSession; +export async function getCurrentAuthenticationSessionInfo(environmentService: IWorkbenchEnvironmentService, productService: IProductService): Promise<{ id: string, accessToken: string, providerId: string } | undefined> { + if (environmentService.options?.credentialsProvider) { + const authenticationSessionValue = await environmentService.options.credentialsProvider.getPassword(`${productService.urlProtocol}.login`, 'account'); + if (authenticationSessionValue) { + const authenticationSession: { id: string, accessToken: string, providerId: string } = JSON.parse(authenticationSessionValue); + if (authenticationSession + && isString(authenticationSession.id) + && isString(authenticationSession.accessToken) + && isString(authenticationSession.providerId) + ) { + return authenticationSession; + } } } return undefined; diff --git a/src/vs/workbench/services/userData/browser/userDataInit.ts b/src/vs/workbench/services/userData/browser/userDataInit.ts index 6500ffcbcc7..def22f7a04c 100644 --- a/src/vs/workbench/services/userData/browser/userDataInit.ts +++ b/src/vs/workbench/services/userData/browser/userDataInit.ts @@ -19,7 +19,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IRequestService } from 'vs/platform/request/common/request'; import { CONFIGURATION_SYNC_STORE_KEY, IUserDataSyncStoreClient, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; import { URI } from 'vs/base/common/uri'; -import { getAuthenticationSession } from 'vs/workbench/services/authentication/browser/authenticationService'; +import { getCurrentAuthenticationSessionInfo } from 'vs/workbench/services/authentication/browser/authenticationService'; import { getSyncAreaLabel } from 'vs/workbench/services/userDataSync/common/userDataSync'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -87,7 +87,7 @@ export class UserDataInitializationService implements IUserDataInitializationSer let authenticationSession; try { - authenticationSession = await getAuthenticationSession(this.environmentService.options.credentialsProvider, this.productService); + authenticationSession = await getCurrentAuthenticationSessionInfo(this.environmentService, this.productService); } catch (error) { this.logService.error(error); } diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index d5d2504a3e0..72286eeed77 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -11,7 +11,7 @@ import { AuthenticationSession, AuthenticationSessionsChangeEvent } from 'vs/edi import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; import { flatten, equals } from 'vs/base/common/arrays'; -import { getAuthenticationProviderActivationEvent, getAuthenticationSession, IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; +import { getAuthenticationProviderActivationEvent, getCurrentAuthenticationSessionInfo, IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; import { IQuickInputService, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { IStorageService, IWorkspaceStorageChangeEvent, StorageScope } from 'vs/platform/storage/common/storage'; @@ -153,7 +153,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat } private async initialize(): Promise { - const authenticationSession = this.environmentService.options?.credentialsProvider ? await getAuthenticationSession(this.environmentService.options?.credentialsProvider, this.productService) : undefined; + const authenticationSession = this.environmentService.options?.credentialsProvider ? await getCurrentAuthenticationSessionInfo(this.environmentService, this.productService) : undefined; if (this.currentSessionId === undefined && this.useWorkbenchSessionId && (authenticationSession?.id || this.environmentService.options?.authenticationSessionId)) { this.currentSessionId = authenticationSession?.id || this.environmentService.options?.authenticationSessionId; this.useWorkbenchSessionId = false; From 160fd51f715866aca98aae0b02326f25fb48bc4e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 20 Aug 2020 10:49:16 +0200 Subject: [PATCH 371/736] use type --- .../browser/authenticationService.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/services/authentication/browser/authenticationService.ts b/src/vs/workbench/services/authentication/browser/authenticationService.ts index 8346c283c32..8791faa4681 100644 --- a/src/vs/workbench/services/authentication/browser/authenticationService.ts +++ b/src/vs/workbench/services/authentication/browser/authenticationService.ts @@ -21,17 +21,18 @@ import { isString } from 'vs/base/common/types'; export function getAuthenticationProviderActivationEvent(id: string): string { return `onAuthenticationRequest:${id}`; } -export async function getCurrentAuthenticationSessionInfo(environmentService: IWorkbenchEnvironmentService, productService: IProductService): Promise<{ id: string, accessToken: string, providerId: string } | undefined> { +export type AuthenticationSessionInfo = { readonly id: string, readonly accessToken: string, readonly providerId: string }; +export async function getCurrentAuthenticationSessionInfo(environmentService: IWorkbenchEnvironmentService, productService: IProductService): Promise { if (environmentService.options?.credentialsProvider) { const authenticationSessionValue = await environmentService.options.credentialsProvider.getPassword(`${productService.urlProtocol}.login`, 'account'); if (authenticationSessionValue) { - const authenticationSession: { id: string, accessToken: string, providerId: string } = JSON.parse(authenticationSessionValue); - if (authenticationSession - && isString(authenticationSession.id) - && isString(authenticationSession.accessToken) - && isString(authenticationSession.providerId) + const authenticationSessionInfo: AuthenticationSessionInfo = JSON.parse(authenticationSessionValue); + if (authenticationSessionInfo + && isString(authenticationSessionInfo.id) + && isString(authenticationSessionInfo.accessToken) + && isString(authenticationSessionInfo.providerId) ) { - return authenticationSession; + return authenticationSessionInfo; } } } From 135b6da83746477b2983fee879122ceefa70014d Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 20 Aug 2020 11:05:01 +0200 Subject: [PATCH 372/736] debug: do not show toolbar while initialising fixes #84228 --- .../contrib/debug/browser/callStackView.ts | 4 ++-- .../contrib/debug/browser/debugToolBar.ts | 2 +- .../contrib/debug/browser/debugViewlet.ts | 15 ++++++++++----- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 23009759915..301ac0bf63e 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -982,10 +982,10 @@ function getActions(instantiationService: IInstantiationService, element: IDebug } -class StopAction extends Action { +export class StopAction extends Action { constructor( - private readonly session: IDebugSession, + private readonly session: IDebugSession | null, @ICommandService private readonly commandService: ICommandService ) { super(`action.${STOP_ID}`, STOP_LABEL, 'debug-action codicon-debug-stop'); diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index 54c7593b579..6658ae4b1f5 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -91,7 +91,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { this.updateScheduler = this._register(new RunOnceScheduler(() => { const state = this.debugService.state; const toolBarLocation = this.configurationService.getValue('debug').toolBarLocation; - if (state === State.Inactive || toolBarLocation === 'docked' || toolBarLocation === 'hidden') { + if (state === State.Inactive || state === State.Initializing || toolBarLocation === 'docked' || toolBarLocation === 'hidden') { return this.hide(); } diff --git a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts index d32774028e5..d20d93a8fac 100644 --- a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts @@ -34,6 +34,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { ShowViewletAction } from 'vs/workbench/browser/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { StopAction } from 'vs/workbench/contrib/debug/browser/callStackView'; export class DebugViewPaneContainer extends ViewPaneContainer { @@ -65,9 +66,7 @@ export class DebugViewPaneContainer extends ViewPaneContainer { super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService); this.updateToolBarScheduler = this._register(new RunOnceScheduler(() => { - if (this.configurationService.getValue('debug').toolBarLocation === 'docked') { - this.updateTitleArea(); - } + this.updateTitleArea(); }, 20)); // When there are potential updates to the docked debug toolbar we need to update it @@ -119,6 +118,11 @@ export class DebugViewPaneContainer extends ViewPaneContainer { return this._register(this.instantiationService.createInstance(OpenDebugConsoleAction, OpenDebugConsoleAction.ID, OpenDebugConsoleAction.LABEL)); } + @memoize + private get stopAction(): StopAction { + return this._register(this.instantiationService.createInstance(StopAction, null)); + } + @memoize private get selectAndStartAction(): SelectAndStartAction { return this._register(this.instantiationService.createInstance(SelectAndStartAction, SelectAndStartAction.ID, nls.localize('startAdditionalSession', "Start Additional Session"))); @@ -150,12 +154,13 @@ export class DebugViewPaneContainer extends ViewPaneContainer { return [this.toggleReplAction]; } - return [this.startAction, this.configureAction, this.toggleReplAction]; + const firstAction = this.debugService.state === State.Initializing ? this.stopAction : this.startAction; + return [firstAction, this.configureAction, this.toggleReplAction]; } get showInitialDebugActions(): boolean { const state = this.debugService.state; - return state === State.Inactive || this.configurationService.getValue('debug').toolBarLocation !== 'docked'; + return state === State.Inactive || state === State.Initializing || this.configurationService.getValue('debug').toolBarLocation !== 'docked'; } getSecondaryActions(): IAction[] { From 0e0a0657cd3d0a6699fd84b3cc945ffa59799127 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 20 Aug 2020 11:42:13 +0200 Subject: [PATCH 373/736] fix join with command ordering fyi @roblourens --- .../workbench/contrib/notebook/browser/contrib/coreActions.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 90000b71cf2..ee6efac4503 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -1478,7 +1478,8 @@ registerAction2(class extends NotebookCellAction { menu: { id: MenuId.NotebookCellTitle, when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE), - group: '2_edit', + group: CellOverflowToolbarGroups.Edit, + order: 10 } }); } @@ -1503,6 +1504,7 @@ registerAction2(class extends NotebookCellAction { id: MenuId.NotebookCellTitle, when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE), group: CellOverflowToolbarGroups.Edit, + order: 11 } }); } From a9624db8e16252ede8863c7f5293e45343d0c964 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 20 Aug 2020 11:58:30 +0200 Subject: [PATCH 374/736] Web: remote indicator API (#105069) * remote - refactor indicator a bit for better readability * remote indicator - more refactorings and cleanup * web api - shuffle some things around * remote indicator - add remote transition indicator * update remote indicator API * :lipstick: --- src/vs/code/browser/workbench/workbench.ts | 74 +++++- src/vs/workbench/browser/contextkeys.ts | 2 - .../contrib/remote/browser/remote.ts | 4 +- .../contrib/remote/browser/remoteIndicator.ts | 245 +++++++++++------- src/vs/workbench/workbench.web.api.ts | 52 +++- 5 files changed, 266 insertions(+), 111 deletions(-) diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index c629f7fffa1..2c3adf03e36 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -3,7 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWorkbenchConstructionOptions, create, URI, Emitter, UriComponents, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace } from 'vs/workbench/workbench.web.api'; +import { IWorkbenchConstructionOptions, create, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IRemoteIndicator, ICommand, IHomeIndicator } from 'vs/workbench/workbench.web.api'; +import product from 'vs/platform/product/common/product'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { Event, Emitter } from 'vs/base/common/event'; import { generateUuid } from 'vs/base/common/uuid'; import { CancellationToken } from 'vs/base/common/cancellation'; import { streamToBuffer } from 'vs/base/common/buffer'; @@ -276,6 +279,51 @@ class WorkspaceProvider implements IWorkspaceProvider { } } +class RemoteIndicator implements IRemoteIndicator { + + readonly onDidChange = Event.None; + + readonly label: string; + readonly tooltip: string; + readonly command: string | undefined; + + readonly commandImpl: ICommand | undefined = undefined; + + constructor(workspace: IWorkspace) { + let repositoryOwner: string | undefined = undefined; + let repositoryName: string | undefined = undefined; + + if (workspace) { + let uri: URI | undefined = undefined; + if (isFolderToOpen(workspace)) { + uri = workspace.folderUri; + } else if (isWorkspaceToOpen(workspace)) { + uri = workspace.workspaceUri; + } + + if (uri?.scheme === 'github' || uri?.scheme === 'codespace') { + [repositoryOwner, repositoryName] = uri.authority.split('+'); + } + } + + if (repositoryName && repositoryOwner) { + this.label = localize('openInDesktopLabel', "$(remote) Open in Desktop"); + this.tooltip = localize('openInDesktopTooltip', "Open in Desktop"); + this.command = '_web.openInDesktop'; + this.commandImpl = { + id: this.command, + handler: () => { + const protocol = product.quality === 'stable' ? 'vscode' : 'vscode-insiders'; + window.open(`${protocol}://vscode.git/clone?url=${encodeURIComponent(`https://github.com/${repositoryOwner}/${repositoryName}.git`)}`); + } + }; + } else { + this.label = localize('playgroundLabel', "Web Playground"); + this.tooltip = this.label; + } + } +} + (function () { // Find config by checking for DOM @@ -343,14 +391,28 @@ class WorkspaceProvider implements IWorkspaceProvider { } } + // Home Indicator + const homeIndicator: IHomeIndicator = { + href: 'https://github.com/Microsoft/vscode', + icon: 'code', + title: localize('home', "Home") + }; + + // Commands + const commands: ICommand[] = []; + + // Remote indicator + const remoteIndicator = new RemoteIndicator(workspace); + if (remoteIndicator.commandImpl) { + commands.push(remoteIndicator.commandImpl); + } + // Finally create workbench create(document.body, { ...config, - homeIndicator: { - href: 'https://github.com/Microsoft/vscode', - icon: 'code', - title: localize('home', "Home") - }, + homeIndicator, + commands, + remoteIndicator, workspaceProvider: new WorkspaceProvider(workspace, payload), urlCallbackProvider: new PollingURLCallbackProvider(), credentialsProvider: new LocalStorageCredentialsProvider() diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts index 118699c1986..1de01f036f3 100644 --- a/src/vs/workbench/browser/contextkeys.ts +++ b/src/vs/workbench/browser/contextkeys.ts @@ -21,8 +21,6 @@ import { PanelPositionContext } from 'vs/workbench/common/panel'; import { getRemoteName } from 'vs/platform/remote/common/remoteHosts'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; -export const Deprecated_RemoteAuthorityContext = new RawContextKey('remoteAuthority', ''); - export const RemoteNameContext = new RawContextKey('remoteName', ''); export const RemoteConnectionState = new RawContextKey<'' | 'initializing' | 'disconnected' | 'connected'>('remoteConnectionState', ''); diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index e9a398cc506..0446e6aa795 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -53,7 +53,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Event } from 'vs/base/common/event'; import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { RemoteWindowActiveIndicator } from 'vs/workbench/contrib/remote/browser/remoteIndicator'; +import { RemoteStatusIndicator } from 'vs/workbench/contrib/remote/browser/remoteIndicator'; import { inQuickPickContextKeyValue } from 'vs/workbench/browser/quickaccess'; import { Codicon, registerIcon } from 'vs/base/common/codicons'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; @@ -838,4 +838,4 @@ class RemoteAgentConnectionStatusListener implements IWorkbenchContribution { const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchContributionsRegistry.registerWorkbenchContribution(RemoteAgentConnectionStatusListener, LifecyclePhase.Eventually); -workbenchContributionsRegistry.registerWorkbenchContribution(RemoteWindowActiveIndicator, LifecyclePhase.Starting); +workbenchContributionsRegistry.registerWorkbenchContribution(RemoteStatusIndicator, LifecyclePhase.Starting); diff --git a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts index 7aec94c4f7e..63cd68da412 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { STATUS_BAR_HOST_NAME_BACKGROUND, STATUS_BAR_HOST_NAME_FOREGROUND } from 'vs/workbench/common/theme'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, dispose } from 'vs/base/common/lifecycle'; import { MenuId, IMenuService, MenuItemAction, IMenu, MenuRegistry, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor, IStatusbarEntry } from 'vs/workbench/services/statusbar/common/statusbar'; @@ -21,88 +21,112 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { RemoteConnectionState, Deprecated_RemoteAuthorityContext } from 'vs/workbench/browser/contextkeys'; +import { RemoteConnectionState } from 'vs/workbench/browser/contextkeys'; import { isWeb } from 'vs/base/common/platform'; import { once } from 'vs/base/common/functional'; -const WINDOW_ACTIONS_COMMAND_ID = 'workbench.action.remote.showMenu'; -const CLOSE_REMOTE_COMMAND_ID = 'workbench.action.remote.close'; -const SHOW_CLOSE_REMOTE_COMMAND_ID = !isWeb; // web does not have a "Close Remote" command +export class RemoteStatusIndicator extends Disposable implements IWorkbenchContribution { -export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenchContribution { + private static REMOTE_ACTIONS_COMMAND_ID = 'workbench.action.remote.showMenu'; + private static CLOSE_REMOTE_COMMAND_ID = 'workbench.action.remote.close'; + private static SHOW_CLOSE_REMOTE_COMMAND_ID = !isWeb; // web does not have a "Close Remote" command - private windowIndicatorEntry: IStatusbarEntryAccessor | undefined; - private windowCommandMenu: IMenu; - private hasWindowActions: boolean = false; - private remoteAuthority: string | undefined; + private remoteStatusEntry: IStatusbarEntryAccessor | undefined; + + private remoteMenu = this._register(this.menuService.createMenu(MenuId.StatusBarWindowIndicatorMenu, this.contextKeyService)); + private hasRemoteActions = false; + + private remoteAuthority = this.environmentService.configuration.remoteAuthority; private connectionState: 'initializing' | 'connected' | 'disconnected' | undefined = undefined; + private connectionStateContextKey = RemoteConnectionState.bindTo(this.contextKeyService); constructor( @IStatusbarService private readonly statusbarService: IStatusbarService, - @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @ILabelService private readonly labelService: ILabelService, @IContextKeyService private contextKeyService: IContextKeyService, @IMenuService private menuService: IMenuService, @IQuickInputService private readonly quickInputService: IQuickInputService, @ICommandService private readonly commandService: ICommandService, - @IExtensionService extensionService: IExtensionService, - @IRemoteAgentService remoteAgentService: IRemoteAgentService, - @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService, - @IHostService hostService: IHostService + @IExtensionService private readonly extensionService: IExtensionService, + @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, + @IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService, + @IHostService private readonly hostService: IHostService ) { super(); - this.windowCommandMenu = this.menuService.createMenu(MenuId.StatusBarWindowIndicatorMenu, this.contextKeyService); - this._register(this.windowCommandMenu); + // Set initial connection state + if (this.remoteAuthority) { + this.connectionState = 'initializing'; + this.connectionStateContextKey.set(this.connectionState); + } + this.registerActions(); + this.registerListeners(); + + this.updateWhenInstalledExtensionsRegistered(); + this.updateRemoteStatusIndicator(); + } + + private registerActions(): void { const category = { value: nls.localize('remote.category', "Remote"), original: 'Remote' }; + + // Show Remote Menu const that = this; registerAction2(class extends Action2 { constructor() { super({ - id: WINDOW_ACTIONS_COMMAND_ID, + id: RemoteStatusIndicator.REMOTE_ACTIONS_COMMAND_ID, category, title: { value: nls.localize('remote.showMenu', "Show Remote Menu"), original: 'Show Remote Menu' }, f1: true, }); } - run = () => that.showIndicatorActions(that.windowCommandMenu); + run = () => that.showRemoteMenu(that.remoteMenu); }); - this.remoteAuthority = environmentService.configuration.remoteAuthority; - Deprecated_RemoteAuthorityContext.bindTo(this.contextKeyService).set(this.remoteAuthority || ''); + // Close Remote Connection + if (RemoteStatusIndicator.SHOW_CLOSE_REMOTE_COMMAND_ID && this.remoteAuthority) { + registerAction2(class extends Action2 { + constructor() { + super({ + id: RemoteStatusIndicator.CLOSE_REMOTE_COMMAND_ID, + category, + title: { value: nls.localize('remote.close', "Close Remote Connection"), original: 'Close Remote Connection' }, + f1: true + }); + } + run = () => that.remoteAuthority && that.hostService.openWindow({ forceReuseWindow: true }); + }); + MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '6_close', + command: { + id: RemoteStatusIndicator.CLOSE_REMOTE_COMMAND_ID, + title: nls.localize({ key: 'miCloseRemote', comment: ['&& denotes a mnemonic'] }, "Close Re&&mote Connection") + }, + order: 3.5 + }); + } + } + + private registerListeners(): void { + + // Menu changes + this._register(this.remoteMenu.onDidChange(() => this.updateRemoteActions())); + + // Update indicator when formatter changes as it may have an impact on the remote label + this._register(this.labelService.onDidChangeFormatters(() => this.updateRemoteStatusIndicator())); + + // Update based on remote indicator changes if any + const remoteIndicator = this.environmentService.options?.remoteIndicator; + if (remoteIndicator) { + this._register(remoteIndicator.onDidChange(() => this.updateRemoteStatusIndicator())); + } + + // Listen to changes of the connection if (this.remoteAuthority) { - - if (SHOW_CLOSE_REMOTE_COMMAND_ID) { - registerAction2(class extends Action2 { - constructor() { - super({ - id: CLOSE_REMOTE_COMMAND_ID, - category, - title: { value: nls.localize('remote.close', "Close Remote Connection"), original: 'Close Remote Connection' }, - f1: true - }); - } - run = () => that.remoteAuthority && hostService.openWindow({ forceReuseWindow: true }); - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '6_close', - command: { - id: CLOSE_REMOTE_COMMAND_ID, - title: nls.localize({ key: 'miCloseRemote', comment: ['&& denotes a mnemonic'] }, "Close Re&&mote Connection") - }, - order: 3.5 - }); - } - - // Pending entry until extensions are ready - this.renderWindowIndicator('$(sync~spin) ' + nls.localize('host.open', "Opening Remote..."), undefined, WINDOW_ACTIONS_COMMAND_ID); - this.connectionState = 'initializing'; - RemoteConnectionState.bindTo(this.contextKeyService).set(this.connectionState); - - const connection = remoteAgentService.getConnection(); + const connection = this.remoteAgentService.getConnection(); if (connection) { this._register(connection.onDidStateChange((e) => { switch (e.type) { @@ -119,72 +143,106 @@ export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenc })); } } + } - extensionService.whenInstalledExtensionsRegistered().then(_ => { - if (this.remoteAuthority) { - this._register(this.labelService.onDidChangeFormatters(e => this.updateWindowIndicator())); - remoteAuthorityResolverService.resolveAuthority(this.remoteAuthority).then(() => this.setDisconnected(false), () => this.setDisconnected(true)); - } - this._register(this.windowCommandMenu.onDidChange(e => this.updateWindowActions())); - this.updateWindowIndicator(); - }); + private async updateWhenInstalledExtensionsRegistered(): Promise { + await this.extensionService.whenInstalledExtensionsRegistered(); + + const remoteAuthority = this.remoteAuthority; + if (remoteAuthority) { + + // Try to resolve the authority to figure out connection state + (async () => { + try { + await this.remoteAuthorityResolverService.resolveAuthority(remoteAuthority); + + this.setDisconnected(false); + } catch (error) { + this.setDisconnected(true); + } + })(); + } + + this.updateRemoteStatusIndicator(); } private setDisconnected(isDisconnected: boolean): void { const newState = isDisconnected ? 'disconnected' : 'connected'; if (this.connectionState !== newState) { this.connectionState = newState; - RemoteConnectionState.bindTo(this.contextKeyService).set(this.connectionState); - Deprecated_RemoteAuthorityContext.bindTo(this.contextKeyService).set(isDisconnected ? `disconnected/${this.remoteAuthority!}` : this.remoteAuthority!); - this.updateWindowIndicator(); + this.connectionStateContextKey.set(this.connectionState); + + this.updateRemoteStatusIndicator(); } } - private updateWindowIndicator(): void { - const windowActionCommand = (this.remoteAuthority || this.windowCommandMenu.getActions().length) ? WINDOW_ACTIONS_COMMAND_ID : undefined; - if (this.remoteAuthority) { + private updateRemoteActions() { + const newHasWindowActions = this.remoteMenu.getActions().length > 0; + if (newHasWindowActions !== this.hasRemoteActions) { + this.hasRemoteActions = newHasWindowActions; + + this.updateRemoteStatusIndicator(); + } + } + + private updateRemoteStatusIndicator(): void { + + // Remote indicator: show if provided via options + const remoteIndicator = this.environmentService.options?.remoteIndicator; + if (remoteIndicator) { + this.renderRemoteStatusIndicator(remoteIndicator.label, remoteIndicator.tooltip, remoteIndicator.command); + } + + // Remote Authority: show connection state + else if (this.remoteAuthority) { const hostLabel = this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.remoteAuthority) || this.remoteAuthority; - if (this.connectionState !== 'disconnected') { - this.renderWindowIndicator(`$(remote) ${hostLabel}`, nls.localize('host.tooltip', "Editing on {0}", hostLabel), windowActionCommand); - } else { - this.renderWindowIndicator(`$(alert) ${nls.localize('disconnectedFrom', "Disconnected from")} ${hostLabel}`, nls.localize('host.tooltipDisconnected', "Disconnected from {0}", hostLabel), windowActionCommand); - } - } else { - if (windowActionCommand) { - this.renderWindowIndicator(`$(remote)`, nls.localize('noHost.tooltip', "Open a remote window"), windowActionCommand); - } else if (this.windowIndicatorEntry) { - this.windowIndicatorEntry.dispose(); - this.windowIndicatorEntry = undefined; + switch (this.connectionState) { + case 'initializing': + this.renderRemoteStatusIndicator(`$(sync~spin) ${nls.localize('host.open', "Opening Remote...")}`, nls.localize('host.open', "Opening Remote...")); + break; + case 'disconnected': + this.renderRemoteStatusIndicator(`$(alert) ${nls.localize('disconnectedFrom', "Disconnected from {0}", hostLabel)}`, nls.localize('host.tooltipDisconnected', "Disconnected from {0}", hostLabel)); + break; + default: + this.renderRemoteStatusIndicator(`$(remote) ${hostLabel}`, nls.localize('host.tooltip', "Editing on {0}", hostLabel)); } } + + // Remote Extensions Installed: offer the indicator to show actions + else if (this.remoteMenu.getActions().length > 0) { + this.renderRemoteStatusIndicator(`$(remote)`, nls.localize('noHost.tooltip', "Open a Remote Window")); + } + + // No Remote Extensions: hide status indicator + else { + dispose(this.remoteStatusEntry); + this.remoteStatusEntry = undefined; + } } - private updateWindowActions() { - const newHasWindowActions = this.windowCommandMenu.getActions().length > 0; - if (newHasWindowActions !== this.hasWindowActions) { - this.hasWindowActions = newHasWindowActions; - this.updateWindowIndicator(); + private renderRemoteStatusIndicator(text: string, tooltip?: string, command?: string): void { + const name = nls.localize('remoteHost', "Remote Host"); + if (typeof command !== 'string' && this.remoteMenu.getActions().length > 0) { + command = RemoteStatusIndicator.REMOTE_ACTIONS_COMMAND_ID; } - } - private renderWindowIndicator(text: string, tooltip?: string, command?: string): void { const properties: IStatusbarEntry = { backgroundColor: themeColorFromId(STATUS_BAR_HOST_NAME_BACKGROUND), color: themeColorFromId(STATUS_BAR_HOST_NAME_FOREGROUND), - ariaLabel: nls.localize('remote', "Remote"), + ariaLabel: name, text, tooltip, command }; - if (this.windowIndicatorEntry) { - this.windowIndicatorEntry.update(properties); + + if (this.remoteStatusEntry) { + this.remoteStatusEntry.update(properties); } else { - this.windowIndicatorEntry = this.statusbarService.addEntry(properties, 'status.host', nls.localize('status.host', "Remote Host"), StatusbarAlignment.LEFT, Number.MAX_VALUE /* first entry */); + this.remoteStatusEntry = this.statusbarService.addEntry(properties, 'status.host', name, StatusbarAlignment.LEFT, Number.MAX_VALUE /* first entry */); } } - private showIndicatorActions(menu: IMenu) { - + private showRemoteMenu(menu: IMenu) { const actions = menu.getActions(); const items: (IQuickPickItem | IQuickPickSeparator)[] = []; @@ -192,6 +250,7 @@ export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenc if (items.length) { items.push({ type: 'separator' }); } + for (let action of actionGroup[1]) { if (action instanceof MenuItemAction) { let label = typeof action.item.title === 'string' ? action.item.title : action.item.title.value; @@ -199,6 +258,7 @@ export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenc const category = typeof action.item.category === 'string' ? action.item.category : action.item.category.value; label = nls.localize('cat.title', "{0}: {1}", category, label); } + items.push({ type: 'item', id: action.item.id, @@ -208,13 +268,14 @@ export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenc } } - if (SHOW_CLOSE_REMOTE_COMMAND_ID && this.remoteAuthority) { + if (RemoteStatusIndicator.SHOW_CLOSE_REMOTE_COMMAND_ID && this.remoteAuthority) { if (items.length) { items.push({ type: 'separator' }); } + items.push({ type: 'item', - id: CLOSE_REMOTE_COMMAND_ID, + id: RemoteStatusIndicator.CLOSE_REMOTE_COMMAND_ID, label: nls.localize('closeRemote.title', 'Close Remote Connection') }); } @@ -227,8 +288,10 @@ export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenc if (selectedItems.length === 1) { this.commandService.executeCommand(selectedItems[0].id!); } + quickPick.hide(); })); + quickPick.show(); } } diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 2bd2d89baea..9331e328ea3 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -121,6 +121,32 @@ interface IHomeIndicator { title: string; } +interface IRemoteIndicator { + + /** + * Triggering this event will cause the remote indicator to update. + */ + onDidChange: Event; + + /** + * Label of the remote indicator may include octicons + * e.g. `$(remote) label` + */ + label: string; + + /** + * Tooltip of the remote indicator should not include + * octicons and be descriptive. + */ + tooltip: string; + + /** + * If provided, overrides the default command that + * is executed when clicking on the remote indicator. + */ + command?: string; +} + interface IDefaultSideBarLayout { visible?: boolean; containers?: ({ @@ -204,6 +230,11 @@ interface IWorkbenchConstructionOptions { */ readonly connectionToken?: string; + /** + * Session id of the current authenticated user + */ + readonly authenticationSessionId?: string; + /** * An endpoint to serve iframe content ("webview") from. This is required * to provide full security isolation from the workbench host. @@ -231,6 +262,11 @@ interface IWorkbenchConstructionOptions { */ readonly tunnelProvider?: ITunnelProvider; + /** + * Endpoints to be used for proxying authentication code exchange calls in the browser. + */ + readonly codeExchangeProxyEndpoints?: { [providerId: string]: string } + //#endregion @@ -247,11 +283,6 @@ interface IWorkbenchConstructionOptions { */ userDataProvider?: IFileSystemProvider; - /** - * Session id of the current authenticated user - */ - readonly authenticationSessionId?: string; - /** * Enables user data sync by default and syncs into the current authenticated user account using the provided [authenticationSessionId}(#authenticationSessionId). */ @@ -345,6 +376,11 @@ interface IWorkbenchConstructionOptions { */ readonly productConfiguration?: Partial; + /** + * Optional override for properties of the remote window indicator in the status bar. + */ + readonly remoteIndicator?: IRemoteIndicator; + //#endregion @@ -360,11 +396,6 @@ interface IWorkbenchConstructionOptions { */ readonly driver?: boolean; - /** - * Endpoints to be used for proxying authentication code exchange calls in the browser. - */ - readonly codeExchangeProxyEndpoints?: { [providerId: string]: string } - //#endregion } @@ -504,6 +535,7 @@ export { // Branding IHomeIndicator, IProductConfiguration, + IRemoteIndicator, // Default layout IDefaultView, From 89a24111903148970e785a1e46e6aa9b7de6b696 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 20 Aug 2020 11:57:07 +0200 Subject: [PATCH 375/736] chore - make notebook cell language as readonly --- 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 68c62e7af19..c240f250bb4 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1284,7 +1284,7 @@ declare module 'vscode' { readonly uri: Uri; readonly cellKind: CellKind; readonly document: TextDocument; - language: string; + readonly language: string; outputs: CellOutput[]; metadata: NotebookCellMetadata; } From 88b7db28155b2c9f8fd9a71300687c601d44b945 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 20 Aug 2020 12:57:30 +0200 Subject: [PATCH 376/736] workaround (fix?) for https://github.com/microsoft/vscode/issues/105073 @rebornix --- src/vs/editor/browser/editorBrowser.ts | 5 +++++ src/vs/editor/browser/widget/codeEditorWidget.ts | 4 ++++ src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 3f9eef1b816..a5d5511f041 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -580,6 +580,11 @@ export interface ICodeEditor extends editorCommon.IEditor { */ getRawOptions(): IEditorOptions; + /** + * @internal + */ + getOverflowWidgetsDomNode(): HTMLElement | undefined; + /** * @internal */ diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index aec462f71f0..17539558c91 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -379,6 +379,10 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return this._configuration.getRawOptions(); } + public getOverflowWidgetsDomNode(): HTMLElement | undefined { + return this._overflowWidgetsDomNode; + } + public getConfiguredWordAtPosition(position: Position): IWordAtPosition | null { if (!this._modelData) { return null; diff --git a/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts b/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts index 09a18fcf097..5dd98c444c7 100644 --- a/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts +++ b/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts @@ -37,7 +37,7 @@ export class EmbeddedCodeEditorWidget extends CodeEditorWidget { @INotificationService notificationService: INotificationService, @IAccessibilityService accessibilityService: IAccessibilityService ) { - super(domElement, parentEditor.getRawOptions(), {}, instantiationService, codeEditorService, commandService, contextKeyService, themeService, notificationService, accessibilityService); + super(domElement, { ...parentEditor.getRawOptions(), overflowWidgetsDomNode: parentEditor.getOverflowWidgetsDomNode() }, {}, instantiationService, codeEditorService, commandService, contextKeyService, themeService, notificationService, accessibilityService); this._parentEditor = parentEditor; this._overwriteOptions = options; From 73d583f7595bd8a2496231998df7423aa6ab5866 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 20 Aug 2020 13:33:28 +0200 Subject: [PATCH 377/736] Move task definition variable resolving to be early Part of #81007 --- .../workbench/api/browser/mainThreadTask.ts | 8 ++++--- .../tasks/browser/terminalTaskSystem.ts | 22 ++++++++++++++++--- .../workbench/contrib/tasks/common/tasks.ts | 6 +++-- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTask.ts b/src/vs/workbench/api/browser/mainThreadTask.ts index d94b7f3dfcb..6b25a273488 100644 --- a/src/vs/workbench/api/browser/mainThreadTask.ts +++ b/src/vs/workbench/api/browser/mainThreadTask.ts @@ -419,9 +419,11 @@ export class MainThreadTask implements MainThreadTaskShape { if (event.kind === TaskEventKind.Start) { const execution = TaskExecutionDTO.from(task.getTaskExecution()); let resolvedDefinition: TaskDefinitionDTO | undefined; - if (execution.task && execution.task.execution && CustomExecutionDTO.is(execution.task.execution)) { - resolvedDefinition = await this._configurationResolverService.resolveWithInteractionReplace(task.getWorkspaceFolder(), - execution.task.definition, 'tasks'); + if (execution.task && execution.task.execution && CustomExecutionDTO.is(execution.task.execution) && event.resolvedVariables) { + const dictionary: IStringDictionary = {}; + Array.from(event.resolvedVariables.entries()).forEach(entry => dictionary[entry[0]] = entry[1]); + resolvedDefinition = await this._configurationResolverService.resolveAny(task.getWorkspaceFolder(), + execution.task.definition, dictionary); } this._proxy.$onDidStartTask(execution, event.terminalId!, resolvedDefinition); } else if (event.kind === TaskEventKind.ProcessStarted) { diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index f6993a12c3c..e35865f75fc 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -81,12 +81,12 @@ class InstanceManager { class VariableResolver { - constructor(public workspaceFolder: IWorkspaceFolder | undefined, public taskSystemInfo: TaskSystemInfo | undefined, private _values: Map, private _service: IConfigurationResolverService | undefined) { + constructor(public workspaceFolder: IWorkspaceFolder | undefined, public taskSystemInfo: TaskSystemInfo | undefined, public readonly values: Map, private _service: IConfigurationResolverService | undefined) { } resolve(value: string): string { 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)); + let result = this.values.get(match.substring(2, match.length - 1)); if ((result !== undefined) && (result !== null)) { return result; } @@ -840,7 +840,7 @@ export class TerminalTaskSystem implements ITaskSystem { }, (_error) => { // The process never got ready. Need to think how to handle this. }); - this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Start, task, terminal.id)); + this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Start, task, terminal.id, resolver.values)); const mapKey = task.getMapKey(); this.busyTasks[mapKey] = task; this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Active, task)); @@ -1331,6 +1331,22 @@ export class TerminalTaskSystem implements ITaskSystem { this.collectCommandVariables(variables, task.command, task); } this.collectMatcherVariables(variables, task.configurationProperties.problemMatchers); + + if (task.command.runtime === RuntimeType.CustomExecution && CustomTask.is(task)) { + this.collectDefinitionVariables(variables, task._source.config.element); + } + } + + private collectDefinitionVariables(variables: Set, definition: any): void { + for (const key in definition) { + if (Types.isString(definition[key])) { + this.collectVariables(variables, definition[key]); + } else if (Types.isArray(definition[key])) { + definition[key].forEach((element: any) => this.collectDefinitionVariables(variables, element)); + } else if (Types.isObject(definition[key])) { + this.collectDefinitionVariables(variables, definition[key]); + } + } } private collectCommandVariables(variables: Set, command: CommandConfiguration, task: CustomTask | ContributedTask): void { diff --git a/src/vs/workbench/contrib/tasks/common/tasks.ts b/src/vs/workbench/contrib/tasks/common/tasks.ts index 7ec7668a9f6..a88910f481f 100644 --- a/src/vs/workbench/contrib/tasks/common/tasks.ts +++ b/src/vs/workbench/contrib/tasks/common/tasks.ts @@ -1067,6 +1067,7 @@ export interface TaskEvent { exitCode?: number; terminalId?: number; __task?: Task; + resolvedVariables?: Map; } export const enum TaskRunSource { @@ -1078,10 +1079,10 @@ 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, task: Task, terminalId?: number): TaskEvent; + export function create(kind: TaskEventKind.Start, task: Task, terminalId?: number, resolvedVariables?: Map): 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, processIdOrExitCodeOrTerminalId?: number): TaskEvent { + export function create(kind: TaskEventKind, task?: Task, processIdOrExitCodeOrTerminalId?: number, resolvedVariables?: Map): TaskEvent { if (task) { let result: TaskEvent = { kind: kind, @@ -1096,6 +1097,7 @@ export namespace TaskEvent { }; if (kind === TaskEventKind.Start) { result.terminalId = processIdOrExitCodeOrTerminalId; + result.resolvedVariables = resolvedVariables; } else if (kind === TaskEventKind.ProcessStarted) { result.processId = processIdOrExitCodeOrTerminalId; } else if (kind === TaskEventKind.ProcessEnded) { From 362e49228c8ba5ae1d267f2789a474129e8c37ca Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 20 Aug 2020 13:41:04 +0200 Subject: [PATCH 378/736] renameOnType: debounce update request, trigger update on changes. Fixes #86845. Fixes #86845 --- src/vs/editor/contrib/rename/onTypeRename.ts | 177 +++++++++++------- .../contrib/rename/test/onTypeRename.test.ts | 43 ++--- 2 files changed, 129 insertions(+), 91 deletions(-) diff --git a/src/vs/editor/contrib/rename/onTypeRename.ts b/src/vs/editor/contrib/rename/onTypeRename.ts index 4503e3ee5b2..ebe4de0bbe3 100644 --- a/src/vs/editor/contrib/rename/onTypeRename.ts +++ b/src/vs/editor/contrib/rename/onTypeRename.ts @@ -16,7 +16,7 @@ import { ITextModel, IModelDeltaDecoration, TrackedRangeStickiness, IIdentifiedS import { CancellationToken } from 'vs/base/common/cancellation'; import { IRange, Range } from 'vs/editor/common/core/range'; import { OnTypeRenameProviderRegistry } from 'vs/editor/common/modes'; -import { first, createCancelablePromise, CancelablePromise, RunOnceScheduler } from 'vs/base/common/async'; +import { first, createCancelablePromise, CancelablePromise, Delayer } from 'vs/base/common/async'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { ContextKeyExpr, RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; @@ -50,13 +50,16 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr private readonly _visibleContextKey: IContextKey; + private _rangeUpdateTriggerPromise: Promise | null; + private _rangeSyncTriggerPromise: Promise | null; + private _currentRequest: CancelablePromise | null; private _currentRequestPosition: Position | null; + private _currentRequestModelVersion: number | null; private _currentDecorations: string[]; // The one at index 0 is the reference one private _stopPattern: RegExp; private _ignoreChangeEvent: boolean; - private readonly _updateMirrors: RunOnceScheduler; private readonly _localToDispose = this._register(new DisposableStore()); @@ -68,13 +71,18 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr this._editor = editor; this._enabled = false; this._visibleContextKey = CONTEXT_ONTYPE_RENAME_INPUT_VISIBLE.bindTo(contextKeyService); - this._currentRequest = null; - this._currentRequestPosition = null; + this._currentDecorations = []; this._stopPattern = /\s/; this._ignoreChangeEvent = false; this._localToDispose = this._register(new DisposableStore()); - this._updateMirrors = this._register(new RunOnceScheduler(() => this._doUpdateMirrors(), 0)); + + this._rangeUpdateTriggerPromise = null; + this._rangeSyncTriggerPromise = null; + + this._currentRequest = null; + this._currentRequestPosition = null; + this._currentRequestModelVersion = null; this._register(this._editor.onDidChangeModel(() => this.reinitialize())); @@ -98,81 +106,67 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr this._enabled = isEnabled; - this.clearLinkedUI(); + this.clearRanges(); this._localToDispose.clear(); if (!isEnabled || model === null) { return; } + const rangeUpdateScheduler = new Delayer(200); + const triggerRangeUpdate = () => { + this._rangeUpdateTriggerPromise = rangeUpdateScheduler.trigger(() => this.updateRanges()); + }; + const rangeSyncScheduler = new Delayer(0); + const triggerRangeSync = (decorations: string[]) => { + this._rangeSyncTriggerPromise = rangeSyncScheduler.trigger(() => this._syncRanges(decorations)); + }; this._localToDispose.add(this._editor.onDidChangeCursorPosition((e) => { - if (e.secondaryPositions.length > 0) { - this.clearLinkedUI(); // multi-cursor, don't run - return; - } - // no regions, run - if (this._currentDecorations.length === 0) { - this.updateLinkedUI(e.position); - return; - } - - // has cached regions - const primaryRange = model.getDecorationRange(this._currentDecorations[0]); - - // just moving cursor around, don't run again - if (primaryRange && Range.containsPosition(primaryRange, e.position)) { - return; - } - - this._updateMirrors.cancel(); - - // moving cursor out of primary region, run - this.updateLinkedUI(e.position); + triggerRangeUpdate(); })); this._localToDispose.add(this._editor.onDidChangeModelContent((e) => { - if (this._ignoreChangeEvent) { - return; - } - if (this._currentDecorations.length === 0) { - // nothing to do - return; - } - if (e.isUndoing || e.isRedoing) { - return; - } - for (const change of e.changes) { - if (this._stopPattern.test(change.text)) { - this.clearLinkedUI(); - return; + if (!this._ignoreChangeEvent) { + if (this._currentDecorations.length > 0) { + const referenceRange = model.getDecorationRange(this._currentDecorations[0]); + if (referenceRange && e.changes.every(c => referenceRange.intersectRanges(c.range))) { + triggerRangeSync(this._currentDecorations); + return; + } } } - this._updateMirrors.schedule(); + triggerRangeUpdate(); })); - this.updateLinkedUI(); + this._localToDispose.add({ + dispose: () => { + rangeUpdateScheduler.cancel(); + rangeSyncScheduler.cancel(); + } + }); + this.updateRanges(); } - private _doUpdateMirrors(): void { + private _syncRanges(decorations: string[]): void { // dalayed invocation, make sure we're still on - if (!this._editor.hasModel() || this._currentDecorations.length === 0) { + if (!this._editor.hasModel() || decorations !== this._currentDecorations || decorations.length === 0) { // nothing to do return; } const model = this._editor.getModel(); - const referenceRange = model.getDecorationRange(this._currentDecorations[0]); + const referenceRange = model.getDecorationRange(decorations[0]); if (!referenceRange || referenceRange.startLineNumber !== referenceRange.endLineNumber) { - return this.clearLinkedUI(); + return this.clearRanges(); } const referenceValue = model.getValueInRange(referenceRange); if (this._stopPattern.test(referenceValue)) { - return this.clearLinkedUI(); + return this.clearRanges(); } let edits: IIdentifiedSingleEditOperation[] = []; - for (let i = 1, len = this._currentDecorations.length; i < len; i++) { - const mirrorRange = model.getDecorationRange(this._currentDecorations[i]); + for (let i = 1, len = decorations.length; i < len; i++) { + const mirrorRange = model.getDecorationRange(decorations[i]); if (!mirrorRange) { continue; } @@ -221,11 +215,11 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr } public dispose(): void { - this.clearLinkedUI(); + this.clearRanges(); super.dispose(); } - public clearLinkedUI(): void { + public clearRanges(): void { this._visibleContextKey.set(false); this._currentDecorations = this._editor.deltaDecorations(this._currentDecorations, []); if (this._currentRequest) { @@ -233,23 +227,45 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr this._currentRequest = null; this._currentRequestPosition = null; } - this._updateMirrors.cancel(); } - public get currentRequest(): Promise { - return this._currentRequest || Promise.resolve(); + public get currentUpdateTriggerPromise(): Promise { + return this._rangeUpdateTriggerPromise || Promise.resolve(); } - public async updateLinkedUI(position: Position | null = this._editor.getPosition(), force = false): Promise { + public get currentSyncTriggerPromise(): Promise { + return this._rangeSyncTriggerPromise || Promise.resolve(); + } + + public async updateRanges(force = false): Promise { + if (!this._editor.hasModel()) { + this.clearRanges(); + return; + } + + const position = this._editor.getPosition(); + if (!this._enabled && !force || this._editor.getSelections().length > 1) { + // disabled or multicursor + this.clearRanges(); + return; + } + const model = this._editor.getModel(); - if (!this._enabled && !force || !model || !position) { - this.clearLinkedUI(); - return; - } - if (this._currentRequestPosition && (this._currentRequestPosition.equals(position))) { - return; + const modelVersionId = model.getVersionId(); + if (this._currentRequestPosition && this._currentRequestModelVersion === modelVersionId) { + if (position.equals(this._currentRequestPosition)) { + return; // same position + } + if (this._currentDecorations && this._currentDecorations.length > 0) { + const range = model.getDecorationRange(this._currentDecorations[0]); + if (range && range.containsPosition(position)) { + return; // just moving inside the existing primary range + } + } } + this._currentRequestPosition = position; + this._currentRequestModelVersion = modelVersionId; const request = createCancelablePromise(async token => { try { const response = await getOnTypeRenameRanges(model, position, token); @@ -257,6 +273,9 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr return; } this._currentRequest = null; + if (modelVersionId !== model.getVersionId()) { + return; + } let ranges: IRange[] = []; if (response?.ranges) { @@ -281,7 +300,7 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr if (!foundReferenceRange) { // Cannot do on type rename if the ranges are not where the cursor is... - this.clearLinkedUI(); + this.clearRanges(); return; } @@ -294,12 +313,34 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr } if (this._currentRequest === request || !this._currentRequest) { // stop if we are still the latest request - this.clearLinkedUI(); + this.clearRanges(); } } }); this._currentRequest = request; + return request; } + + // private printDecorators(model: ITextModel) { + // return this._currentDecorations.map(d => { + // const range = model.getDecorationRange(d); + // if (range) { + // return this.printRange(range); + // } + // return 'invalid'; + // }).join(','); + // } + + // private printChanges(changes: IModelContentChange[]) { + // return changes.map(c => { + // return `${this.printRange(c.range)} - ${c.text}`; + // } + // ).join(','); + // } + + // private printRange(range: IRange) { + // return `${range.startLineNumber},${range.startColumn}/${range.endLineNumber},${range.endColumn}`; + // } } export class OnTypeRenameAction extends EditorAction { @@ -337,10 +378,10 @@ export class OnTypeRenameAction extends EditorAction { return super.runCommand(accessor, args); } - run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { + run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { const controller = OnTypeRenameContribution.get(editor); if (controller) { - return Promise.resolve(controller.updateLinkedUI(editor.getPosition(), true)); + return Promise.resolve(controller.updateRanges(true)); } return Promise.resolve(); } @@ -350,7 +391,7 @@ const OnTypeRenameCommand = EditorCommand.bindToContribution x.clearLinkedUI(), + handler: x => x.clearRanges(), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, weight: KeybindingWeight.EditorContrib + 99, diff --git a/src/vs/editor/contrib/rename/test/onTypeRename.test.ts b/src/vs/editor/contrib/rename/test/onTypeRename.test.ts index 384a5523081..2d2703df752 100644 --- a/src/vs/editor/contrib/rename/test/onTypeRename.test.ts +++ b/src/vs/editor/contrib/rename/test/onTypeRename.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { Position } from 'vs/editor/common/core/position'; +import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { Handler } from 'vs/editor/common/editorCommon'; import * as modes from 'vs/editor/common/modes'; @@ -14,6 +14,8 @@ import { OnTypeRenameContribution } from 'vs/editor/contrib/rename/onTypeRename' import { createTestCodeEditor, ITestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands'; +import { ITextModel } from 'vs/editor/common/model'; +import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/model/wordHelper'; const mockFile = URI.parse('test:somefile.ttt'); const mockFileSelector = { scheme: 'test' }; @@ -53,16 +55,22 @@ suite('On type rename', () => { function testCase( name: string, - initialState: { text: string | string[], ranges: Range[], stopPattern?: RegExp }, + initialState: { text: string | string[], stopPattern?: RegExp }, operations: (editor: TestEditor) => Promise, expectedEndText: string | string[] ) { test(name, async () => { disposables.add(modes.OnTypeRenameProviderRegistry.register(mockFileSelector, { - stopPattern: initialState.stopPattern || /^\s/, + stopPattern: initialState.stopPattern || /\s/, - provideOnTypeRenameRanges() { - return initialState.ranges; + provideOnTypeRenameRanges(model: ITextModel, pos: IPosition) { + const wordAtPos = model.getWordAtPosition(pos); + if (wordAtPos) { + const matches = model.findMatches(wordAtPos.word, false, false, true, USUAL_WORD_SEPARATORS, false); + assert.ok(matches.length > 0); + return matches.map(m => m.range); + } + return []; } })); @@ -76,19 +84,15 @@ suite('On type rename', () => { const testEditor: TestEditor = { setPosition(pos: Position) { editor.setPosition(pos); - return ontypeRenameContribution.currentRequest; + return ontypeRenameContribution.currentUpdateTriggerPromise; }, setSelection(sel: IRange) { editor.setSelection(sel); - return ontypeRenameContribution.currentRequest; + return ontypeRenameContribution.currentUpdateTriggerPromise; }, trigger(source: string | null | undefined, handlerId: string, payload: any) { editor.trigger(source, handlerId, payload); - return new Promise((s, e) => { - setTimeout(() => { - s(); - }, 0); - }); + return ontypeRenameContribution.currentSyncTriggerPromise; }, undo() { CoreEditingCommands.Undo.runEditorCommand(null, editor, null); @@ -114,11 +118,7 @@ suite('On type rename', () => { } const state = { - text: '', - ranges: [ - new Range(1, 2, 1, 5), - new Range(1, 8, 1, 11), - ] + text: '' }; /** @@ -299,7 +299,7 @@ suite('On type rename', () => { const state3 = { ...state, - stopPattern: /^s/ + stopPattern: /s/ }; testCase('Breakout with stop pattern - insert', state3, async (editor) => { @@ -357,7 +357,8 @@ suite('On type rename', () => { testCase('Delete - left word then undo', state, async (editor) => { const pos = new Position(1, 5); await editor.setPosition(pos); - editor.trigger('keyboard', 'deleteWordLeft', {}); + await editor.trigger('keyboard', 'deleteWordLeft', {}); + editor.undo(); editor.undo(); }, ''); @@ -430,10 +431,6 @@ suite('On type rename', () => { text: [ '', '' - ], - ranges: [ - new Range(1, 2, 1, 5), - new Range(2, 3, 2, 6), ] }; From 640caaef05830b39dead82d4915ed3a61675c782 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 20 Aug 2020 13:48:42 +0200 Subject: [PATCH 379/736] Finalize task custom execution variable resolving Fixes #81007 --- src/vs/vscode.d.ts | 5 +++-- src/vs/vscode.proposed.d.ts | 14 -------------- src/vs/workbench/api/browser/mainThreadTask.ts | 4 ++-- src/vs/workbench/api/common/extHost.api.impl.ts | 1 - src/vs/workbench/api/common/extHost.protocol.ts | 2 +- src/vs/workbench/api/common/extHostTask.ts | 2 +- src/vs/workbench/api/common/extHostTypes.ts | 10 +++++----- 7 files changed, 12 insertions(+), 26 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 488b4e077dd..8a6c2307d02 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -6123,9 +6123,10 @@ declare module 'vscode' { * [Pseudoterminal.close](#Pseudoterminal.close). When the task is complete fire * [Pseudoterminal.onDidClose](#Pseudoterminal.onDidClose). * @param process The [Pseudoterminal](#Pseudoterminal) to be used by the task to display output. - * @param callback The callback that will be called when the task is started by a user. + * @param callback The callback that will be called when the task is started by a user. Any ${} style variables that + * were in the task definition will be resolved and passed into the callback. */ - constructor(callback: () => Thenable); + constructor(callback: (resolvedDefinition: TaskDefinition) => Thenable); } /** diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index c240f250bb4..cab19daf4c1 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -981,7 +981,6 @@ declare module 'vscode' { } //#endregion - //#region CustomExecution: https://github.com/microsoft/vscode/issues/81007 /** * A task to execute */ @@ -989,19 +988,6 @@ declare module 'vscode' { detail?: string; } - export class CustomExecution2 extends CustomExecution { - /** - * Constructs a CustomExecution task object. The callback will be executed the task is run, at which point the - * extension should return the Pseudoterminal it will "run in". The task should wait to do further execution until - * [Pseudoterminal.open](#Pseudoterminal.open) is called. Task cancellation should be handled using - * [Pseudoterminal.close](#Pseudoterminal.close). When the task is complete fire - * [Pseudoterminal.onDidClose](#Pseudoterminal.onDidClose). - * @param callback The callback that will be called when the task is started by a user. - */ - constructor(callback: (resolvedDefinition: TaskDefinition) => Thenable); - } - //#endregion - //#region Task presentation group: https://github.com/microsoft/vscode/issues/47265 export interface TaskPresentationOptions { /** diff --git a/src/vs/workbench/api/browser/mainThreadTask.ts b/src/vs/workbench/api/browser/mainThreadTask.ts index 6b25a273488..350ce8e8753 100644 --- a/src/vs/workbench/api/browser/mainThreadTask.ts +++ b/src/vs/workbench/api/browser/mainThreadTask.ts @@ -418,8 +418,8 @@ export class MainThreadTask implements MainThreadTaskShape { const task = event.__task!; if (event.kind === TaskEventKind.Start) { const execution = TaskExecutionDTO.from(task.getTaskExecution()); - let resolvedDefinition: TaskDefinitionDTO | undefined; - if (execution.task && execution.task.execution && CustomExecutionDTO.is(execution.task.execution) && event.resolvedVariables) { + let resolvedDefinition: TaskDefinitionDTO = execution.task!.definition; + if (execution.task?.execution && CustomExecutionDTO.is(execution.task.execution) && event.resolvedVariables) { const dictionary: IStringDictionary = {}; Array.from(event.resolvedVariables.entries()).forEach(entry => dictionary[entry[0]] = entry[1]); resolvedDefinition = await this._configurationResolverService.resolveAny(task.getWorkspaceFolder(), diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index f697d498e35..314ef18f902 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1039,7 +1039,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I ExtensionMode: extHostTypes.ExtensionMode, ExtensionRuntime: extHostTypes.ExtensionRuntime, CustomExecution: extHostTypes.CustomExecution, - CustomExecution2: extHostTypes.CustomExecution, FileChangeType: extHostTypes.FileChangeType, FileSystemError: extHostTypes.FileSystemError, FileType: files.FileType, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 512dbb8d5bc..391a80730a7 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1449,7 +1449,7 @@ export interface ExtHostSCMShape { export interface ExtHostTaskShape { $provideTasks(handle: number, validTypes: { [key: string]: boolean; }): Thenable; $resolveTask(handle: number, taskDTO: tasks.TaskDTO): Thenable; - $onDidStartTask(execution: tasks.TaskExecutionDTO, terminalId: number, resolvedDefinition?: tasks.TaskDefinitionDTO): void; + $onDidStartTask(execution: tasks.TaskExecutionDTO, terminalId: number, resolvedDefinition: tasks.TaskDefinitionDTO): void; $onDidStartTaskProcess(value: tasks.TaskProcessStartedDTO): void; $onDidEndTaskProcess(value: tasks.TaskProcessEndedDTO): void; $OnDidEndTask(execution: tasks.TaskExecutionDTO): void; diff --git a/src/vs/workbench/api/common/extHostTask.ts b/src/vs/workbench/api/common/extHostTask.ts index f77a8626067..cb118977cbd 100644 --- a/src/vs/workbench/api/common/extHostTask.ts +++ b/src/vs/workbench/api/common/extHostTask.ts @@ -475,7 +475,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape, IExtHostTask return this._onDidExecuteTask.event; } - public async $onDidStartTask(execution: tasks.TaskExecutionDTO, terminalId: number, resolvedDefinition?: tasks.TaskDefinitionDTO): Promise { + public async $onDidStartTask(execution: tasks.TaskExecutionDTO, terminalId: number, resolvedDefinition: tasks.TaskDefinitionDTO): Promise { const customExecution: types.CustomExecution | undefined = this._providedCustomExecutions2.get(execution.id); if (customExecution) { if (this._activeCustomExecutions2.get(execution.id) !== undefined) { diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index db91ee1e124..5d6b6dc15a2 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1831,20 +1831,20 @@ export enum TaskScope { Workspace = 2 } -export class CustomExecution implements vscode.CustomExecution2 { - private _callback: (resolvedDefintion?: vscode.TaskDefinition) => Thenable; - constructor(callback: (resolvedDefintion?: vscode.TaskDefinition) => Thenable) { +export class CustomExecution implements vscode.CustomExecution { + private _callback: (resolvedDefintion: vscode.TaskDefinition) => Thenable; + constructor(callback: (resolvedDefintion: vscode.TaskDefinition) => Thenable) { this._callback = callback; } public computeId(): string { return 'customExecution' + generateUuid(); } - public set callback(value: (resolvedDefintion?: vscode.TaskDefinition) => Thenable) { + public set callback(value: (resolvedDefintion: vscode.TaskDefinition) => Thenable) { this._callback = value; } - public get callback(): ((resolvedDefintion?: vscode.TaskDefinition) => Thenable) { + public get callback(): ((resolvedDefintion: vscode.TaskDefinition) => Thenable) { return this._callback; } } From 94364b7fb7ab18c7006472ea92101697fbceb0ba Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 20 Aug 2020 14:26:47 +0200 Subject: [PATCH 380/736] initialize keybindings and snippets after restored phase --- src/vs/workbench/services/userData/browser/userDataInit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/userData/browser/userDataInit.ts b/src/vs/workbench/services/userData/browser/userDataInit.ts index def22f7a04c..d3fc176f380 100644 --- a/src/vs/workbench/services/userData/browser/userDataInit.ts +++ b/src/vs/workbench/services/userData/browser/userDataInit.ts @@ -166,5 +166,5 @@ class InitializeOtherResourcesContribution implements IWorkbenchContribution { if (isWeb) { const workbenchRegistry = Registry.as(Extensions.Workbench); - workbenchRegistry.registerWorkbenchContribution(InitializeOtherResourcesContribution, LifecyclePhase.Eventually); + workbenchRegistry.registerWorkbenchContribution(InitializeOtherResourcesContribution, LifecyclePhase.Restored); } From 71e4d1dc2bb0839963847eab48fcf8c9cb8cb3d2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 20 Aug 2020 15:19:38 +0200 Subject: [PATCH 381/736] :up: web playground --- resources/web/code-web.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/web/code-web.js b/resources/web/code-web.js index bfe3f9f9a21..5c8046693a6 100644 --- a/resources/web/code-web.js +++ b/resources/web/code-web.js @@ -27,7 +27,7 @@ const BUILTIN_MARKETPLACE_EXTENSIONS_ROOT = path.join(APP_ROOT, '.build', 'built const WEB_DEV_EXTENSIONS_ROOT = path.join(APP_ROOT, '.build', 'builtInWebDevExtensions'); const WEB_MAIN = path.join(APP_ROOT, 'src', 'vs', 'code', 'browser', 'workbench', 'workbench-dev.html'); -const WEB_PLAYGROUND_VERSION = '0.0.1'; +const WEB_PLAYGROUND_VERSION = '0.0.2'; const args = minimist(process.argv, { boolean: [ From 1b7035d05057a8d13b81b596ee7ab64ad4ab5309 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 20 Aug 2020 15:32:54 +0200 Subject: [PATCH 382/736] web - add a quality change handler --- src/vs/code/browser/workbench/workbench.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 2c3adf03e36..ebf75434a9f 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWorkbenchConstructionOptions, create, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IRemoteIndicator, ICommand, IHomeIndicator } from 'vs/workbench/workbench.web.api'; +import { IWorkbenchConstructionOptions, create, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IRemoteIndicator, ICommand, IHomeIndicator, IProductQualityChangeHandler } from 'vs/workbench/workbench.web.api'; import product from 'vs/platform/product/common/product'; import { URI, UriComponents } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; @@ -407,12 +407,28 @@ class RemoteIndicator implements IRemoteIndicator { commands.push(remoteIndicator.commandImpl); } + // Product Quality Change Handler + const productQualityChangeHandler: IProductQualityChangeHandler = (quality) => { + let queryString = `quality=${quality}`; + + // Save all other query params we might have + const query = new URL(document.location.href).searchParams; + query.forEach((value, key) => { + if (key !== 'quality') { + queryString += `&${key}=${value}`; + } + }); + + window.location.href = `${window.location.origin}?${queryString}`; + }; + // Finally create workbench create(document.body, { ...config, homeIndicator, commands, remoteIndicator, + productQualityChangeHandler, workspaceProvider: new WorkspaceProvider(workspace, payload), urlCallbackProvider: new PollingURLCallbackProvider(), credentialsProvider: new LocalStorageCredentialsProvider() From 038e1a93b995e166f2f8efdbc4d3c38d8841c6b0 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Thu, 20 Aug 2020 10:17:58 -0700 Subject: [PATCH 383/736] fixes #104967 --- src/vs/workbench/contrib/update/browser/update.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/update/browser/update.ts b/src/vs/workbench/contrib/update/browser/update.ts index 5d590de2e93..d42000cbbee 100644 --- a/src/vs/workbench/contrib/update/browser/update.ts +++ b/src/vs/workbench/contrib/update/browser/update.ts @@ -435,7 +435,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu group: '6_update', command: { id: 'update.downloadNow', - title: nls.localize('download update', "Download Update") + title: nls.localize('download update_1', "Download Update (1)") }, when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.AvailableForDownload) }); @@ -456,7 +456,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu group: '6_update', command: { id: 'update.install', - title: nls.localize('installUpdate...', "Install Update...") + title: nls.localize('installUpdate...', "Install Update... (1)") }, when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Downloaded) }); From 509bc25f0287158fd95054ed19fddb3c17c94f57 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 20 Aug 2020 10:53:42 -0700 Subject: [PATCH 384/736] notebook editor worker service. --- .eslintrc.json | 14 ++ .../notebook/browser/notebook.contribution.ts | 7 +- .../notebook/browser/notebookDiffEditor.ts | 109 +++------ .../contrib/notebook/common/notebookCommon.ts | 7 +- .../common/services/notebookSimpleWorker.ts | 207 +++++++++++++++++ .../common/services/notebookWorkerService.ts | 25 ++ .../services/notebookWorkerServiceImpl.ts | 219 ++++++++++++++++++ 7 files changed, 508 insertions(+), 80 deletions(-) create mode 100644 src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts create mode 100644 src/vs/workbench/contrib/notebook/common/services/notebookWorkerService.ts create mode 100644 src/vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl.ts diff --git a/.eslintrc.json b/.eslintrc.json index 8761c1c1813..df3c5ad560c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -653,6 +653,20 @@ "**/vs/workbench/services/**/{common,browser}/**" ] }, + { + "target": "**/vs/workbench/contrib/notebook/common/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/**/{common,worker}/**", + "**/vs/platform/**/common/**", + "**/vs/editor/**", + "**/vs/workbench/common/**", + "**/vs/workbench/api/common/**", + "**/vs/workbench/services/**/common/**", + "**/vs/workbench/contrib/**/common/**" + ] + }, { "target": "**/vs/workbench/contrib/**/common/**", "restrictions": [ diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index f0087a0b1ef..f80967a2fb1 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -42,8 +42,10 @@ import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { INotebookEditorModelResolverService, NotebookModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; -import { NotebookDiffEditorInput } from './notebookDiffEditorInput'; -import { NotebookDiffEditor } from './notebookDiffEditor'; +import { NotebookDiffEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookDiffEditorInput'; +import { NotebookDiffEditor } from 'vs/workbench/contrib/notebook/browser/notebookDiffEditor'; +import { INotebookEditorWorkerService } from 'vs/workbench/contrib/notebook/common/services/notebookWorkerService'; +import { NotebookEditorWorkerServiceImpl } from 'vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl'; // Editor Contribution @@ -438,6 +440,7 @@ workbenchContributionsRegistry.registerWorkbenchContribution(NotebookContributio workbenchContributionsRegistry.registerWorkbenchContribution(CellContentProvider, LifecyclePhase.Starting); registerSingleton(INotebookService, NotebookService); +registerSingleton(INotebookEditorWorkerService, NotebookEditorWorkerServiceImpl); registerSingleton(INotebookEditorModelResolverService, NotebookModelResolverService, true); const configurationRegistry = Registry.as(Extensions.Configuration); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts index 9d45f6a9fac..40ce3828b8e 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts @@ -16,15 +16,13 @@ import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/note import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { NotebookDiffEditorInput } from './notebookDiffEditorInput'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IDiffResult, LcsDiff } from 'vs/base/common/diff/diff'; -import { CellSequence, INotebookDiffEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookDiffEditorModel, INotebookDiffResult } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookDeltaDecoration } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; import { FileService } from 'vs/platform/files/common/fileService'; import { IFileService } from 'vs/platform/files/common/files'; -import { DiffComputer } from 'vs/editor/common/diff/diffComputer'; import { createDecoration, DECORATIONS, isChangeOrDelete, isChangeOrInsert } from 'vs/editor/browser/widget/diffEditorWidget'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { Color } from 'vs/base/common/color'; @@ -32,6 +30,7 @@ import { IModelDeltaDecoration, IReadonlyTextBuffer } from 'vs/editor/common/mod import { Range } from 'vs/editor/common/core/range'; import { Constants } from 'vs/base/common/uint'; import { defaultInsertColor, defaultRemoveColor, diffInserted, diffRemoved } from 'vs/platform/theme/common/colorRegistry'; +import { INotebookEditorWorkerService } from 'vs/workbench/contrib/notebook/common/services/notebookWorkerService'; export class NotebookDiffEditor extends BaseEditor { static readonly ID: string = 'workbench.editor.notebookDiffEditor'; @@ -52,6 +51,7 @@ export class NotebookDiffEditor extends BaseEditor { @ITelemetryService telemetryService: ITelemetryService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IStorageService storageService: IStorageService, + @INotebookEditorWorkerService private readonly notebookEditorWorkerService: INotebookEditorWorkerService ) { super(NotebookDiffEditor.ID, telemetryService, themeService, storageService); @@ -149,10 +149,9 @@ export class NotebookDiffEditor extends BaseEditor { } private _update(model: INotebookDiffEditorModel) { - const diff = new LcsDiff(new CellSequence(model.original.notebook), new CellSequence(model.modified.notebook)); - const diffResult = diff.ComputeDiff(false); - - this._adjustHeight(diffResult); + this.notebookEditorWorkerService.computeDiff(model.original.notebook.uri, model.modified.notebook.uri).then(diffResult => { + this._adjustHeight(diffResult); + }); } private _setStrategy(newStrategy: DiffEditorWidgetSideBySide): void { @@ -162,13 +161,12 @@ export class NotebookDiffEditor extends BaseEditor { this._strategy = newStrategy; newStrategy.applyColors(this.themeService.getColorTheme()); - - // if (this._diffComputationResult) { - // this._updateDecorations(); - // } } - private _adjustHeight(diffResult: IDiffResult) { + private _adjustHeight(notebookDiffResult: INotebookDiffResult) { + const diffResult = notebookDiffResult.cellsDiff; + const linesDiffResult = notebookDiffResult.linesDiff; + if (!this._widget || !this._originalWidget) { return; } @@ -211,67 +209,37 @@ export class NotebookDiffEditor extends BaseEditor { }; viewLayoutUpdateDisposables.push(...[ - ...originalCells.map(cell => (cell instanceof CodeCellViewModel) ? cell.onDidChangeLayout(e => update()) : (cell as MarkdownCellViewModel).onDidChangeLayout(e => update())), - ...modifiedCells.map(cell => (cell instanceof CodeCellViewModel) ? cell.onDidChangeLayout(e => update()) : (cell as MarkdownCellViewModel).onDidChangeLayout(e => update())), + ...originalCells.map(cell => (cell instanceof CodeCellViewModel) ? cell.onDidChangeLayout(() => update()) : (cell as MarkdownCellViewModel).onDidChangeLayout(() => update())), + ...modifiedCells.map(cell => (cell instanceof CodeCellViewModel) ? cell.onDidChangeLayout(() => update()) : (cell as MarkdownCellViewModel).onDidChangeLayout(() => update())), ]); + + update(); }); - // console.log(diffResult); - diffResult.changes.forEach(change => { - if (change.modifiedLength === 0) { - // deletion ... + linesDiffResult.forEach(diff => { + const originalCell = this._originalWidget!.viewModel!.viewCells.find(cell => cell.handle === diff.originalCellhandle); + const modifiedCell = this._widget!.viewModel!.viewCells.find(cell => cell.handle === diff.modifiedCellhandle); + + if (!originalCell || !modifiedCell) { return; } - if (change.originalLength === 0) { - // insertion - return; - } + const lineDecorations = this._strategy.getEditorsDiffDecorations(diff.lineChanges, false, false, originalCell.textBuffer, modifiedCell.textBuffer); - for (let i = 0, len = Math.min(change.modifiedLength, change.originalLength); i < len; i++) { - let originalIndex = change.originalStart + i; - let modifiedIndex = change.modifiedStart + i; + this._originalWidget?.changeModelDecorations(accessor => { + accessor.deltaDecorations([], [{ + ownerId: diff.originalCellhandle, + decorations: lineDecorations.original.decorations + }]); + }); - const originalCell = this._originalWidget!.viewModel!.viewCells[originalIndex]; - const modifiedCell = this._widget!.viewModel!.viewCells[modifiedIndex]; - - if (originalCell.getText() !== modifiedCell.getText()) { - // console.log(`original cell ${originalIndex} content change`); - const originalLines = originalCell.textBuffer.getLinesContent(); - const modifiedLines = modifiedCell.textBuffer.getLinesContent(); - const diffComputer = new DiffComputer(originalLines, modifiedLines, { - shouldComputeCharChanges: true, - shouldPostProcessCharChanges: true, - shouldIgnoreTrimWhitespace: false, - shouldMakePrettyDiff: true, - maxComputationTime: 5000 - }); - - const lineChanges = diffComputer.computeDiff().changes; - const lineDecorations = this._strategy.getEditorsDiffDecorations(lineChanges, false, false, originalCell.textBuffer, modifiedCell.textBuffer); - - this._originalWidget?.changeModelDecorations(accessor => { - accessor.deltaDecorations([], [{ - ownerId: originalCell.handle, - decorations: lineDecorations.original.decorations - }]); - }); - - this._widget?.changeModelDecorations(accessor => { - accessor.deltaDecorations([], [{ - ownerId: modifiedCell.handle, - decorations: lineDecorations.modified.decorations - }]); - }); - - // console.log(lineDecorations); - - } else { - // console.log(`original cell ${originalIndex} metadata change`); - } - - } + this._widget?.changeModelDecorations(accessor => { + accessor.deltaDecorations([], [{ + ownerId: diff.modifiedCellhandle, + decorations: lineDecorations.modified.decorations + }]); + }); }); this._originalCellDecorations = this._originalWidget.deltaCellDecorations(this._originalCellDecorations, originalDecorations); @@ -362,15 +330,6 @@ export class DiffEditorWidgetSideBySide extends Disposable { } public getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalModel: IReadonlyTextBuffer, modifiedModel: IReadonlyTextBuffer): IEditorsDiffDecorationsWithZones { - // Get view zones - // modifiedWhitespaces = modifiedWhitespaces.sort((a, b) => { - // return a.afterLineNumber - b.afterLineNumber; - // }); - // originalWhitespaces = originalWhitespaces.sort((a, b) => { - // return a.afterLineNumber - b.afterLineNumber; - // }); - // let zones = this._getViewZones(lineChanges, originalWhitespaces, modifiedWhitespaces, originalEditor, modifiedEditor, renderIndicators); - // Get decorations & overview ruler zones let originalDecorations = this._getOriginalEditorDecorations(lineChanges, ignoreTrimWhitespace, renderIndicators, originalModel); let modifiedDecorations = this._getModifiedEditorDecorations(lineChanges, ignoreTrimWhitespace, renderIndicators, modifiedModel); @@ -378,13 +337,9 @@ export class DiffEditorWidgetSideBySide extends Disposable { return { original: { decorations: originalDecorations.decorations, - // overviewZones: originalDecorations.overviewZones, - // zones: zones.original }, modified: { decorations: modifiedDecorations.decorations, - // overviewZones: modifiedDecorations.overviewZones, - // zones: zones.modified } }; } diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index f56e0eee899..8c6541885cb 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -18,7 +18,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Schemas } from 'vs/base/common/network'; import { IRevertOptions } from 'vs/workbench/common/editor'; import { basename } from 'vs/base/common/path'; -import { ISequence } from 'vs/base/common/diff/diff'; +import { IDiffResult, ISequence } from 'vs/base/common/diff/diff'; export enum CellKind { Markdown = 1, @@ -715,3 +715,8 @@ export class CellSequence implements ISequence { return hashValue; } } + +export interface INotebookDiffResult { + cellsDiff: IDiffResult, + linesDiff: { originalCellhandle: number, modifiedCellhandle: number, lineChanges: editorCommon.ILineChange[] }[]; +} diff --git a/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts b/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts new file mode 100644 index 00000000000..65870613c73 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts @@ -0,0 +1,207 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { ISequence, LcsDiff } from 'vs/base/common/diff/diff'; +import { hash } from 'vs/base/common/hash'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { IRequestHandler } from 'vs/base/common/worker/simpleWorker'; +import * as model from 'vs/editor/common/model'; +import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder'; +import { CellKind, ICellDto2, IMainCellDto, INotebookDiffResult, IProcessedOutput, NotebookCellMetadata, NotebookDataDto, NotebookDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { Range } from 'vs/editor/common/core/range'; +import { DiffComputer } from 'vs/editor/common/diff/diffComputer'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { EditorWorkerHost } from 'vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl'; + +class MirrorCell { + private _textBuffer!: model.IReadonlyTextBuffer; + + get textBuffer() { + if (this._textBuffer) { + return this._textBuffer; + } + + const builder = new PieceTreeTextBufferBuilder(); + builder.acceptChunk(Array.isArray(this._source) ? this._source.join('\n') : this._source); + const bufferFactory = builder.finish(true); + this._textBuffer = bufferFactory.create(model.DefaultEndOfLine.LF); + + return this._textBuffer; + } + + private _hash: number | null = null; + + + constructor( + readonly handle: number, + private _source: string | string[], + readonly language: string, + readonly cellKind: CellKind, + readonly outputs: IProcessedOutput[], + readonly metadata?: NotebookCellMetadata + + ) { } + + getFullModelRange() { + const lineCount = this.textBuffer.getLineCount(); + return new Range(1, 1, lineCount, this.textBuffer.getLineLength(lineCount) + 1); + } + + getValue(): string { + const fullRange = this.getFullModelRange(); + const eol = this.textBuffer.getEOL(); + if (eol === '\n') { + return this.textBuffer.getValueInRange(fullRange, model.EndOfLinePreference.LF); + } else { + return this.textBuffer.getValueInRange(fullRange, model.EndOfLinePreference.CRLF); + } + } + + getHashValue(): number { + if (this._hash !== null) { + return this._hash; + } + + this._hash = hash([hash(this.getValue()), this.metadata]); + // this._hash = hash(this.getValue()); + return this._hash; + } +} + +class MirrorNotebookDocument { + constructor( + readonly uri: URI, + readonly cells: MirrorCell[], + readonly languages: string[], + readonly metadata: NotebookDocumentMetadata, + ) { + } +} + +export class CellSequence implements ISequence { + + constructor(readonly textModel: MirrorNotebookDocument) { + } + + getElements(): string[] | number[] | Int32Array { + const hashValue = new Int32Array(this.textModel.cells.length); + for (let i = 0; i < this.textModel.cells.length; i++) { + hashValue[i] = this.textModel.cells[i].getHashValue(); + } + + return hashValue; + } + + getCellHash(cell: ICellDto2) { + const source = Array.isArray(cell.source) ? cell.source.join('\n') : cell.source; + const hashVal = hash([hash(source), cell.metadata]); + return hashVal; + } +} + +export class NotebookEditorSimpleWorker implements IRequestHandler, IDisposable { + _requestHandlerBrand: any; + + private _models: { [uri: string]: MirrorNotebookDocument; }; + + constructor() { + this._models = Object.create(null); + } + dispose(): void { + } + + public acceptNewModel(uri: string, data: NotebookDataDto): void { + this._models[uri] = new MirrorNotebookDocument(URI.parse(uri), data.cells.map(dto => new MirrorCell( + (dto as IMainCellDto).handle, + dto.source, + dto.language, + dto.cellKind, + dto.outputs, + dto.metadata + )), data.languages, data.metadata); + } + + public acceptRemovedModel(strURL: string): void { + if (!this._models[strURL]) { + return; + } + delete this._models[strURL]; + } + + computeDiff(originalUrl: string, modifiedUrl: string): INotebookDiffResult { + const original = this._getModel(originalUrl); + const modified = this._getModel(modifiedUrl); + + const diff = new LcsDiff(new CellSequence(original), new CellSequence(modified)); + const diffResult = diff.ComputeDiff(false); + + let cellLineChanges: { originalCellhandle: number, modifiedCellhandle: number, lineChanges: editorCommon.ILineChange[] }[] = []; + + diffResult.changes.forEach(change => { + if (change.modifiedLength === 0) { + // deletion ... + return; + } + + if (change.originalLength === 0) { + // insertion + return; + } + + for (let i = 0, len = Math.min(change.modifiedLength, change.originalLength); i < len; i++) { + let originalIndex = change.originalStart + i; + let modifiedIndex = change.modifiedStart + i; + + const originalCell = original.cells[originalIndex]; + const modifiedCell = modified.cells[modifiedIndex]; + + if (originalCell.getValue() !== modifiedCell.getValue()) { + // console.log(`original cell ${originalIndex} content change`); + const originalLines = originalCell.textBuffer.getLinesContent(); + const modifiedLines = modifiedCell.textBuffer.getLinesContent(); + const diffComputer = new DiffComputer(originalLines, modifiedLines, { + shouldComputeCharChanges: true, + shouldPostProcessCharChanges: true, + shouldIgnoreTrimWhitespace: false, + shouldMakePrettyDiff: true, + maxComputationTime: 5000 + }); + + const lineChanges = diffComputer.computeDiff().changes; + + cellLineChanges.push({ + originalCellhandle: originalCell.handle, + modifiedCellhandle: modifiedCell.handle, + lineChanges + }); + + // console.log(lineDecorations); + + } else { + // console.log(`original cell ${originalIndex} metadata change`); + } + + } + }); + + return { + cellsDiff: diffResult, + linesDiff: cellLineChanges + }; + } + + protected _getModel(uri: string): MirrorNotebookDocument { + return this._models[uri]; + } +} + +/** + * Called on the worker side + * @internal + */ +export function create(host: EditorWorkerHost): IRequestHandler { + return new NotebookEditorSimpleWorker(); +} + diff --git a/src/vs/workbench/contrib/notebook/common/services/notebookWorkerService.ts b/src/vs/workbench/contrib/notebook/common/services/notebookWorkerService.ts new file mode 100644 index 00000000000..f85fd6f5cc9 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/common/services/notebookWorkerService.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI } from 'vs/base/common/uri'; +import { ILineChange } from 'vs/editor/common/editorCommon'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { INotebookDiffResult } from 'vs/workbench/contrib/notebook/common/notebookCommon'; + +export const ID_NOTEBOOK_EDITOR_WORKER_SERVICE = 'notebookEditorWorkerService'; +export const INotebookEditorWorkerService = createDecorator(ID_NOTEBOOK_EDITOR_WORKER_SERVICE); + +export interface IDiffComputationResult { + quitEarly: boolean; + identical: boolean; + changes: ILineChange[]; +} + +export interface INotebookEditorWorkerService { + readonly _serviceBrand: undefined; + + canComputeDiff(original: URI, modified: URI): boolean; + computeDiff(original: URI, modified: URI): Promise; +} diff --git a/src/vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl.ts b/src/vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl.ts new file mode 100644 index 00000000000..8f12d9ccda4 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl.ts @@ -0,0 +1,219 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable, DisposableStore, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { SimpleWorkerClient } from 'vs/base/common/worker/simpleWorker'; +import { DefaultWorkerFactory } from 'vs/base/worker/defaultWorkerFactory'; +import { INotebookDiffResult } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; +import { NotebookEditorSimpleWorker } from 'vs/workbench/contrib/notebook/common/services/notebookSimpleWorker'; +import { INotebookEditorWorkerService } from 'vs/workbench/contrib/notebook/common/services/notebookWorkerService'; + +export class NotebookEditorWorkerServiceImpl extends Disposable implements INotebookEditorWorkerService { + declare readonly _serviceBrand: undefined; + + private readonly _workerManager: WorkerManager; + + constructor( + @INotebookService notebookService: INotebookService + ) { + super(); + + this._workerManager = this._register(new WorkerManager(notebookService)); + } + canComputeDiff(original: URI, modified: URI): boolean { + throw new Error('Method not implemented.'); + } + + computeDiff(original: URI, modified: URI): Promise { + return this._workerManager.withWorker().then(client => { + return client.computeDiff(original, modified); + }); + } +} + +export class WorkerManager extends Disposable { + private _editorWorkerClient: NotebookWorkerClient | null; + // private _lastWorkerUsedTime: number; + + constructor( + private readonly _notebookService: INotebookService + ) { + super(); + this._editorWorkerClient = null; + // this._lastWorkerUsedTime = (new Date()).getTime(); + } + + withWorker(): Promise { + // this._lastWorkerUsedTime = (new Date()).getTime(); + if (!this._editorWorkerClient) { + this._editorWorkerClient = new NotebookWorkerClient(this._notebookService, 'notebookEditorWorkerService'); + } + return Promise.resolve(this._editorWorkerClient); + } +} + +export interface IWorkerClient { + getProxyObject(): Promise; + dispose(): void; +} + +export class NotebookEditorModelManager extends Disposable { + private _syncedModels: { [modelUrl: string]: IDisposable; } = Object.create(null); + private _syncedModelsLastUsedTime: { [modelUrl: string]: number; } = Object.create(null); + + constructor( + private readonly _proxy: NotebookEditorSimpleWorker, + private readonly _notebookService: INotebookService + ) { + super(); + } + + public ensureSyncedResources(resources: URI[]): void { + for (const resource of resources) { + let resourceStr = resource.toString(); + + if (!this._syncedModels[resourceStr]) { + this._beginModelSync(resource); + } + if (this._syncedModels[resourceStr]) { + this._syncedModelsLastUsedTime[resourceStr] = (new Date()).getTime(); + } + } + } + + private _beginModelSync(resource: URI): void { + let model = this._notebookService.listNotebookDocuments().find(document => document.uri.toString() === resource.toString()); + if (!model) { + return; + } + + let modelUrl = resource.toString(); + + this._proxy.acceptNewModel( + model.uri.toString(), + { + cells: model.cells.map(cell => ({ + handle: cell.handle, + uri: cell.uri, + source: cell.textBuffer.getLinesContent(), + eol: cell.textBuffer.getEOL(), + language: cell.language, + cellKind: cell.cellKind, + outputs: cell.outputs, + metadata: cell.metadata + })), + languages: model.languages, + metadata: model.metadata + } + ); + + const toDispose = new DisposableStore(); + + // TODO, accept Model change + + // toDispose.add(model.onDidChangeContent((e) => { + // this._proxy.acceptModelChanged(modelUrl.toString(), e); + // })); + toDispose.add(model.onWillDispose(() => { + this._stopModelSync(modelUrl); + })); + toDispose.add(toDisposable(() => { + this._proxy.acceptRemovedModel(modelUrl); + })); + + this._syncedModels[modelUrl] = toDispose; + } + + private _stopModelSync(modelUrl: string): void { + let toDispose = this._syncedModels[modelUrl]; + delete this._syncedModels[modelUrl]; + delete this._syncedModelsLastUsedTime[modelUrl]; + dispose(toDispose); + } +} + +export class EditorWorkerHost { + + private readonly _workerClient: NotebookWorkerClient; + + constructor(workerClient: NotebookWorkerClient) { + this._workerClient = workerClient; + } + + // foreign host request + public fhr(method: string, args: any[]): Promise { + return this._workerClient.fhr(method, args); + } +} + +export class NotebookWorkerClient extends Disposable { + private _worker: IWorkerClient | null; + private readonly _workerFactory: DefaultWorkerFactory; + private _modelManager: NotebookEditorModelManager | null; + + + constructor(private readonly _notebookService: INotebookService, label: string) { + super(); + this._workerFactory = new DefaultWorkerFactory(label); + this._worker = null; + this._modelManager = null; + + } + + // foreign host request + public fhr(method: string, args: any[]): Promise { + throw new Error(`Not implemented!`); + } + + computeDiff(original: URI, modified: URI) { + return this._withSyncedResources([original, modified]).then(proxy => { + return proxy.computeDiff(original.toString(), modified.toString()); + }); + } + + private _getOrCreateModelManager(proxy: NotebookEditorSimpleWorker): NotebookEditorModelManager { + if (!this._modelManager) { + this._modelManager = this._register(new NotebookEditorModelManager(proxy, this._notebookService)); + } + return this._modelManager; + } + + protected _withSyncedResources(resources: URI[]): Promise { + return this._getProxy().then((proxy) => { + this._getOrCreateModelManager(proxy).ensureSyncedResources(resources); + return proxy; + }); + } + + private _getOrCreateWorker(): IWorkerClient { + if (!this._worker) { + try { + this._worker = this._register(new SimpleWorkerClient( + this._workerFactory, + 'vs/workbench/contrib/notebook/common/services/notebookSimpleWorker', + new EditorWorkerHost(this) + )); + } catch (err) { + // logOnceWebWorkerWarning(err); + // this._worker = new SynchronousWorkerClient(new EditorSimpleWorker(new EditorWorkerHost(this), null)); + throw (err); + } + } + return this._worker; + } + + protected _getProxy(): Promise { + return this._getOrCreateWorker().getProxyObject().then(undefined, (err) => { + // logOnceWebWorkerWarning(err); + // this._worker = new SynchronousWorkerClient(new EditorSimpleWorker(new EditorWorkerHost(this), null)); + // return this._getOrCreateWorker().getProxyObject(); + throw (err); + }); + } + + +} From c332f2de48ff42b069622cb8b78b0ada660447f6 Mon Sep 17 00:00:00 2001 From: Ken Brownfield Date: Thu, 20 Aug 2020 12:06:27 -0700 Subject: [PATCH 385/736] Suppert \U\u\L\l replace modifiers in global search/replace (see PR#96128) --- .../services/search/common/replace.ts | 85 ++++++++++++++++++- .../search/test/common/replace.test.ts | 6 ++ 2 files changed, 89 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/search/common/replace.ts b/src/vs/workbench/services/search/common/replace.ts index 9dd7567231c..66b67d620bb 100644 --- a/src/vs/workbench/services/search/common/replace.ts +++ b/src/vs/workbench/services/search/common/replace.ts @@ -13,6 +13,7 @@ export class ReplacePattern { private _replacePattern: string; private _hasParameters: boolean = false; private _regExp: RegExp; + private _caseOpsRegExp: RegExp; constructor(replaceString: string, searchPatternInfo: IPatternInfo) constructor(replaceString: string, parseParameters: boolean, regEx: RegExp) @@ -37,6 +38,8 @@ export class ReplacePattern { if (this._regExp.global) { this._regExp = strings.createRegExp(this._regExp.source, true, { matchCase: !this._regExp.ignoreCase, wholeWord: false, multiline: this._regExp.multiline, global: false }); } + + this._caseOpsRegExp = new RegExp(/([^\\]*?)((?:\\[uUlL])+?|)(\$[0-9]+)(.*?)/g); } get hasParameters(): boolean { @@ -60,10 +63,10 @@ export class ReplacePattern { const match = this._regExp.exec(text); if (match) { if (this.hasParameters) { + const replaceString = this.replaceWithCaseOperations(text, this._regExp, this.buildReplaceString(match, preserveCase)); if (match[0] === text) { - return text.replace(this._regExp, this.buildReplaceString(match, preserveCase)); + return replaceString; } - const replaceString = text.replace(this._regExp, this.buildReplaceString(match, preserveCase)); return replaceString.substr(match.index, match[0].length - (text.length - replaceString.length)); } return this.buildReplaceString(match, preserveCase); @@ -72,6 +75,84 @@ export class ReplacePattern { return null; } + /** + * replaceWithCaseOperations applies case operations to relevant replacement strings and applies + * the affected $N arguments. It then passes unaffected $N arguments through to string.replace(). + * + * \u => upper-cases one character in a match. + * \U => upper-cases ALL remaining characters in a match. + * \l => lower-cases one character in a match. + * \L => lower-cases ALL remaining characters in a match. + */ + private replaceWithCaseOperations(text: string, regex: RegExp, replaceString: string): string { + // Short-circuit the common path. + if (!/\\[uUlL]/.test(replaceString)) { + return text.replace(regex, replaceString); + } + // Store the values of the search parameters. + const firstMatch = regex.exec(text); + if (firstMatch === null) { + return text.replace(regex, replaceString); + } + + let patMatch: RegExpExecArray | null; + let newReplaceString = ''; + let lastIndex = 0; + let lastMatch = ''; + // For each annotated $N, perform text processing on the parameters and perform the substitution. + while ((patMatch = this._caseOpsRegExp.exec(replaceString)) !== null) { + lastIndex = patMatch.index; + const fullMatch = patMatch[0]; + lastMatch = fullMatch; + let caseOps = patMatch[2]; // \u, \l\u, etc. + const money = patMatch[3]; // $1, $2, etc. + + if (!caseOps) { + newReplaceString += fullMatch; + continue; + } + const replacement = firstMatch[parseInt(money.slice(1))]; + if (!replacement) { + newReplaceString += fullMatch; + continue; + } + const replacementLen = replacement.length; + + newReplaceString += patMatch[1]; // prefix + caseOps = caseOps.replace(/\\/g, ''); + let i = 0; + for (; i < caseOps.length; i++) { + switch (caseOps[i]) { + case 'U': + newReplaceString += replacement.slice(i).toUpperCase(); + i = replacementLen; + break; + case 'u': + newReplaceString += replacement[i].toUpperCase(); + break; + case 'L': + newReplaceString += replacement.slice(i).toLowerCase(); + i = replacementLen; + break; + case 'l': + newReplaceString += replacement[i].toLowerCase(); + break; + } + } + // Append any remaining replacement string content not covered by case operations. + if (i < replacementLen) { + newReplaceString += replacement.slice(i); + } + + newReplaceString += patMatch[4]; // suffix + } + + // Append any remaining trailing content after the final regex match. + newReplaceString += replaceString.slice(lastIndex + lastMatch.length); + + return text.replace(regex, newReplaceString); + } + public buildReplaceString(matches: string[] | null, preserveCase?: boolean): string { if (preserveCase) { return buildReplaceStringWithCasePreserved(matches, this._replacePattern); diff --git a/src/vs/workbench/services/search/test/common/replace.test.ts b/src/vs/workbench/services/search/test/common/replace.test.ts index 7180e0fb43e..3c18ccb9e14 100644 --- a/src/vs/workbench/services/search/test/common/replace.test.ts +++ b/src/vs/workbench/services/search/test/common/replace.test.ts @@ -140,6 +140,12 @@ suite('Replace Pattern test', () => { assert.equal('cat ()', actual); }); + test('case operations', () => { + let testObject = new ReplacePattern('a\\u$1l\\u\\l\\U$2M$3n', { pattern: 'a(l)l(good)m(e)n', isRegExp: true }); + let actual = testObject.getReplaceString('allgoodmen'); + assert.equal('aLlGoODMen', actual); + }); + test('get replace string for no matches', () => { let testObject = new ReplacePattern('hello', { pattern: 'bla', isRegExp: true }); let actual = testObject.getReplaceString('foo'); From 61f799f53bc11b3f28ace31dc77a335ca447ac3a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 20 Aug 2020 13:59:22 -0700 Subject: [PATCH 386/736] Add proposed webview view API (#104601) Add proposed webview view API For #46585 This adds a new `WebviewView` proposed api to VS Code that lets webview be used inside views. Webview views can be contributed using a contribution point such as : ```json "views": { "explorer": [ { "type": "webview", "id": "cats.cat", "name": "Cats", "visibility": "visible" } ] }, ``` * Use proper activation event * Transparent background * Fix resize observer * Adding documentation * Move webview view to new directory under workbench * Remove resolver By moving the webviews view into their own fodler, I was able to avoid the cycle the resolver was originally introduced for * Use enum in more places * Hook up title and visible properties for webview views * Remove test view * Prefer Thenable * Add unknown view type error to collector --- extensions/image-preview/src/extension.ts | 2 +- src/vs/vscode.proposed.d.ts | 137 +++++++++++++++ .../api/browser/mainThreadWebview.ts | 87 ++++++++-- .../api/browser/viewsExtensionPoint.ts | 43 ++++- .../workbench/api/common/extHost.api.impl.ts | 8 + .../workbench/api/common/extHost.protocol.ts | 8 + src/vs/workbench/api/common/extHostWebview.ts | 156 ++++++++++++++++++ src/vs/workbench/common/views.ts | 2 + .../browser/webviewView.contribution.ts | 9 + .../webviewView/browser/webviewViewPane.ts | 153 +++++++++++++++++ .../webviewView/browser/webviewViewService.ts | 84 ++++++++++ src/vs/workbench/workbench.common.main.ts | 1 + 12 files changed, 674 insertions(+), 16 deletions(-) create mode 100644 src/vs/workbench/contrib/webviewView/browser/webviewView.contribution.ts create mode 100644 src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts create mode 100644 src/vs/workbench/contrib/webviewView/browser/webviewViewService.ts diff --git a/extensions/image-preview/src/extension.ts b/extensions/image-preview/src/extension.ts index 552b32d39b6..10722360dd5 100644 --- a/extensions/image-preview/src/extension.ts +++ b/extensions/image-preview/src/extension.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import { BinarySizeStatusBarEntry } from './binarySizeStatusBarEntry'; import { PreviewManager } from './preview'; import { SizeStatusBarEntry } from './sizeStatusBarEntry'; -import { BinarySizeStatusBarEntry } from './binarySizeStatusBarEntry'; import { ZoomStatusBarEntry } from './zoomStatusBarEntry'; export function activate(context: vscode.ExtensionContext) { diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index cab19daf4c1..1b98220afd5 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1971,4 +1971,141 @@ declare module 'vscode' { notebook: NotebookDocument | undefined; } //#endregion + + + //#region https://github.com/microsoft/vscode/issues/46585 + + /** + * A webview based view. + */ + export interface WebviewView { + /** + * Identifies the type of the webview view, such as `'hexEditor.dataView'`. + */ + readonly viewType: string; + + /** + * The underlying webview for the view. + */ + readonly webview: Webview; + + /** + * View title displayed in the UI. + * + * The view title is initially taken from the extension `package.json` contribution. + */ + title?: string; + + /** + * Event fired when the view is disposed. + * + * Views are disposed of in a few cases: + * + * - When a view is collapsed and `retainContextWhenHidden` has not been set. + * - When a view is hidden by a user. + * + * Trying to use the view after it has been disposed throws an exception. + */ + readonly onDidDispose: Event; + + /** + * Tracks if the webview is currently visible. + * + * Views are visible when they are on the screen and expanded. + */ + readonly visible: boolean; + + /** + * Event fired when the visibility of the view changes + */ + readonly onDidChangeVisibility: Event; + } + + interface WebviewViewResolveContext { + /** + * Persisted state from the webview content. + * + * To save resources, VS Code normally deallocates webview views that are not visible. For example, if the user + * collapse a view or switching to another top level activity, the underlying webview document is deallocates. + * + * You can prevent this behavior by setting `retainContextWhenHidden` in the `WebviewOptions`. However this + * increases resource usage and should be avoided wherever possible. Instead, you can use persisted state to + * save off a webview's state so that it can be quickly recreated as needed. + * + * To save off a persisted state, inside the webview call `acquireVsCodeApi().setState()` with + * any json serializable object. To restore the state again, call `getState()`. For example: + * + * ```js + * // Within the webview + * const vscode = acquireVsCodeApi(); + * + * // Get existing state + * const oldState = vscode.getState() || { value: 0 }; + * + * // Update state + * setState({ value: oldState.value + 1 }) + * ``` + * + * VS Code ensures that the persisted state is saved correctly when a webview is hidden and across + * editor restarts. + */ + readonly state: T | undefined; + } + + /** + * Provider for creating `WebviewView` elements. + */ + export interface WebviewViewProvider { + /** + * Revolves a webview view. + * + * `resolveWebviewView` is called when a view first becomes visible. This may happen when the view is + * first loaded or when the user hides and then shows a view again. + * + * @param webviewView Webview panel to restore. The serializer should take ownership of this panel. The + * provider must set the webview's `.html` and hook up all webview events it is interested in. + * @param context Additional metadata about the view being resolved. + * @param token Cancellation token indicating that the view being provided is no longer needed. + * + * @return Optional thenable indicating that the view has been fully resolved. + */ + resolveWebviewView(webviewView: WebviewView, context: WebviewViewResolveContext, token: CancellationToken): Thenable | void; + } + + namespace window { + /** + * Register a new provider for webview views. + * + * @param viewId Unique id of the view. This should match the `id` from the + * `views` contribution in the package.json. + * @param provider Provider for the webview views. + * + * @return Disposable that unregisters the provider. + */ + export function registerWebviewViewProvider(viewId: string, provider: WebviewViewProvider, options?: { + /** + * Content settings for the webview created for this view. + */ + readonly webviewOptions?: { + /** + * Controls if the webview panel's content (iframe) is kept around even when the panel + * is no longer visible. + * + * Normally the webview's html context is created when the panel becomes visible + * and destroyed when it is hidden. Extensions that have complex state + * or UI can set the `retainContextWhenHidden` to make VS Code keep the webview + * context around, even when the webview moves to a background tab. When a webview using + * `retainContextWhenHidden` becomes hidden, its scripts and other dynamic content are suspended. + * When the panel becomes visible again, the context is automatically restored + * in the exact same state it was in originally. You cannot send messages to a + * hidden webview, even with `retainContextWhenHidden` enabled. + * + * `retainContextWhenHidden` has a high memory overhead and should only be used if + * your panel's context cannot be quickly saved and restored. + */ + readonly retainContextWhenHidden?: boolean; + }; + }): Disposable; + } + //#endregion } diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index 0d21c18efe8..be1f4ed4cbc 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -33,9 +33,10 @@ import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/cus import { CustomDocumentBackupData } from 'vs/workbench/contrib/customEditor/browser/customEditorInputFactory'; import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { CustomTextEditorModel } from 'vs/workbench/contrib/customEditor/common/customTextEditorModel'; -import { WebviewExtensionDescription, WebviewIcons } from 'vs/workbench/contrib/webview/browser/webview'; +import { Webview, WebviewExtensionDescription, WebviewIcons, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; import { ICreateWebViewShowOptions, IWebviewWorkbenchService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; +import { IWebviewViewService, WebviewView } from 'vs/workbench/contrib/webviewView/browser/webviewViewService'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -119,6 +120,10 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma private readonly _proxy: extHostProtocol.ExtHostWebviewsShape; private readonly _webviewInputs = new WebviewInputStore(); private readonly _revivers = new Map(); + + private readonly _webviewViewProviders = new Map(); + private readonly _webviewViews = new Map(); + private readonly _editorProviders = new Map(); private readonly _webviewFromDiffEditorHandles = new Set(); @@ -136,6 +141,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma @IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IBackupFileService private readonly _backupService: IBackupFileService, + @IWebviewViewService private readonly _webviewViewService: IWebviewViewService, ) { super(); @@ -212,7 +218,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma const extension = reviveWebviewExtension(extensionData); const webview = this._webviewWorkbenchService.createWebview(handle, webviewPanelViewType.fromExternal(viewType), title, mainThreadShowOptions, reviveWebviewOptions(options), extension); - this.hookupWebviewEventDelegate(handle, webview); + this.hookupWebviewEventDelegate(handle, webview.webview); this._webviewInputs.add(handle, webview); @@ -234,14 +240,22 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma webview.setName(value); } + public $setWebviewViewTitle(handle: extHostProtocol.WebviewPanelHandle, value: string | undefined): void { + const webviewView = this._webviewViews.get(handle); + if (!webviewView) { + throw new Error('unknown webview view'); + } + webviewView.title = value; + } + public $setIconPath(handle: extHostProtocol.WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents; } | undefined): void { const webview = this.getWebviewInput(handle); webview.iconPath = reviveWebviewIcon(value); } public $setHtml(handle: extHostProtocol.WebviewPanelHandle, value: string): void { - const webview = this.getWebviewInput(handle); - webview.webview.html = value; + const webview = this.getWebview(handle); + webview.html = value; } public $setOptions(handle: extHostProtocol.WebviewPanelHandle, options: modes.IWebviewOptions): void { @@ -285,7 +299,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma const handle = webviewInput.id; this._webviewInputs.add(handle, webviewInput); - this.hookupWebviewEventDelegate(handle, webviewInput); + this.hookupWebviewEventDelegate(handle, webviewInput.webview); let state = undefined; if (webviewInput.webview.state) { @@ -316,6 +330,49 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma this._revivers.delete(viewType); } + public $registerWebviewViewProvider(viewType: string, options?: { retainContextWhenHidden?: boolean }): void { + if (this._webviewViewProviders.has(viewType)) { + throw new Error(`View provider for ${viewType} already registered`); + } + + this._webviewViewService.register(viewType, { + resolve: async (webviewView: WebviewView, cancellation: CancellationToken) => { + this._webviewViews.set(viewType, webviewView); + + const handle = viewType; + this.hookupWebviewEventDelegate(handle, webviewView.webview); + + let state = undefined; + if (webviewView.webview.state) { + try { + state = JSON.parse(webviewView.webview.state); + } catch (e) { + console.error('Could not load webview state', e, webviewView.webview.state); + } + } + + if (options) { + webviewView.webview.options = options; + } + + webviewView.onDidChangeVisibility(visible => { + this._proxy.$onDidChangeWebviewViewVisibility(handle, visible); + }); + + webviewView.onDispose(() => { + this._proxy.$disposeWebviewView(handle); + }); + + try { + await this._proxy.$resolveWebviewView(handle, viewType, state, cancellation); + } catch (error) { + onUnexpectedError(error); + webviewView.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(viewType); + } + } + }); + } + public $registerTextEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: extHostProtocol.CustomTextEditorCapabilities): void { this.registerEditorProvider(ModelType.Text, extensionData, viewType, options, capabilities, true); } @@ -353,7 +410,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma const resource = webviewInput.resource; this._webviewInputs.add(handle, webviewInput); - this.hookupWebviewEventDelegate(handle, webviewInput); + this.hookupWebviewEventDelegate(handle, webviewInput.webview); webviewInput.webview.options = options; webviewInput.webview.extension = extension; @@ -460,14 +517,14 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma model.changeContent(); } - private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewPanelHandle, input: WebviewInput) { + private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewPanelHandle, webview: WebviewOverlay) { const disposables = new DisposableStore(); - disposables.add(input.webview.onDidClickLink((uri) => this.onDidClickLink(handle, uri))); - disposables.add(input.webview.onMessage((message: any) => { this._proxy.$onMessage(handle, message); })); - disposables.add(input.webview.onMissingCsp((extension: ExtensionIdentifier) => this._proxy.$onMissingCsp(handle, extension.value))); + disposables.add(webview.onDidClickLink((uri) => this.onDidClickLink(handle, uri))); + disposables.add(webview.onMessage((message: any) => { this._proxy.$onMessage(handle, message); })); + disposables.add(webview.onMissingCsp((extension: ExtensionIdentifier) => this._proxy.$onMissingCsp(handle, extension.value))); - disposables.add(input.webview.onDispose(() => { + disposables.add(webview.onDispose(() => { disposables.dispose(); this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => { @@ -554,6 +611,14 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma return !!webview.webview.contentOptions.enableCommandUris && link.scheme === Schemas.command; } + private getWebview(handle: extHostProtocol.WebviewPanelHandle): Webview { + const webview = this.tryGetWebviewInput(handle)?.webview ?? this._webviewViews.get(handle)?.webview; + if (!webview) { + throw new Error(`Unknown webview handle:${handle}`); + } + return webview; + } + private getWebviewInput(handle: extHostProtocol.WebviewPanelHandle): WebviewInput { const webview = this.tryGetWebviewInput(handle); if (!webview) { diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 25dadd40aab..73ca03673bc 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -32,6 +32,7 @@ import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneCont import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { Codicon } from 'vs/base/common/codicons'; import { CustomTreeView } from 'vs/workbench/contrib/views/browser/treeView'; +import { WebviewViewPane } from 'vs/workbench/contrib/webviewView/browser/webviewViewPane'; export interface IUserFriendlyViewsContainerDescriptor { id: string; @@ -76,7 +77,15 @@ export const viewsContainersContribution: IJSONSchema = { } }; +enum ViewType { + Tree = 'tree', + Webview = 'webview' +} + + interface IUserFriendlyViewDescriptor { + type?: ViewType; + id: string; name: string; when?: string; @@ -208,11 +217,18 @@ const viewsContribution: IJSONSchema = { } }; -export interface ICustomViewDescriptor extends ITreeViewDescriptor { +export interface ICustomTreeViewDescriptor extends ITreeViewDescriptor { readonly extensionId: ExtensionIdentifier; readonly originalContainerId: string; } +export interface ICustomWebviewViewDescriptor extends IViewDescriptor { + readonly extensionId: ExtensionIdentifier; + readonly originalContainerId: string; +} + +export type ICustomViewDescriptor = ICustomTreeViewDescriptor | ICustomWebviewViewDescriptor; + type ViewContainerExtensionPointType = { [loc: string]: IUserFriendlyViewsContainerDescriptor[] }; const viewsContainersExtensionPoint: IExtensionPoint = ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'viewsContainers', @@ -442,16 +458,24 @@ class ViewsExtensionHandler implements IWorkbenchContribution { const icon = item.icon ? resources.joinPath(extension.description.extensionLocation, item.icon) : undefined; const initialVisibility = this.convertInitialVisibility(item.visibility); - const viewDescriptor = { + + const type = this.getViewType(item.type); + if (!type) { + collector.error(localize('unknownViewType', "Unknown view type `{0}`.", item.type)); + return null; + } + + const viewDescriptor = { + type: type, + ctorDescriptor: type === ViewType.Tree ? new SyncDescriptor(TreeViewPane) : new SyncDescriptor(WebviewViewPane), id: item.id, name: item.name, - ctorDescriptor: new SyncDescriptor(TreeViewPane), when: ContextKeyExpr.deserialize(item.when), containerIcon: icon || viewContainer?.icon, containerTitle: item.contextualTitle || viewContainer?.name, canToggleVisibility: true, canMoveView: true, - treeView: this.instantiationService.createInstance(CustomTreeView, item.id, item.name), + treeView: type === ViewType.Tree ? this.instantiationService.createInstance(CustomTreeView, item.id, item.name) : undefined, collapsed: this.showCollapsed(container) || initialVisibility === InitialVisibility.Collapsed, order: order, extensionId: extension.description.identifier, @@ -461,6 +485,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { hideByDefault: initialVisibility === InitialVisibility.Hidden }; + viewIds.add(viewDescriptor.id); return viewDescriptor; })); @@ -473,6 +498,16 @@ class ViewsExtensionHandler implements IWorkbenchContribution { this.viewsRegistry.registerViews2(allViewDescriptors); } + private getViewType(type: string | undefined): ViewType | undefined { + if (type === ViewType.Webview) { + return ViewType.Webview; + } + if (!type || type === ViewType.Tree) { + return ViewType.Tree; + } + return undefined; + } + private getDefaultViewContainer(): ViewContainer { return this.viewContainersRegistry.get(EXPLORER)!; } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 314ef18f902..25aa14724b4 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -612,6 +612,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }, onDidChangeActiveColorTheme(listener, thisArg?, disposables?) { return extHostTheming.onDidChangeActiveColorTheme(listener, thisArg, disposables); + }, + registerWebviewViewProvider(viewId: string, provider: vscode.WebviewViewProvider, options?: { + webviewOptions?: { + retainContextWhenHidden?: boolean + } + }) { + checkProposedApiEnabled(extension); + return extHostWebviews.registerWebviewViewProvider(extension, viewId, provider, options?.webviewOptions); } }; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 391a80730a7..29c9ebd1f0f 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -629,6 +629,10 @@ export interface MainThreadWebviewsShape extends IDisposable { $onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void; $onContentChange(resource: UriComponents, viewType: string): void; + + $registerWebviewViewProvider(viewType: string, options?: { retainContextWhenHidden?: boolean }): void; + + $setWebviewViewTitle(handle: WebviewPanelHandle, value: string | undefined): void; } export interface WebviewPanelViewStateData { @@ -662,6 +666,10 @@ export interface ExtHostWebviewsShape { $backup(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise; $onMoveCustomEditor(handle: WebviewPanelHandle, newResource: UriComponents, viewType: string): Promise; + + $resolveWebviewView(webviewHandle: WebviewPanelHandle, viewType: string, state: any, cancellation: CancellationToken): Promise; + $onDidChangeWebviewViewVisibility(webviewHandle: WebviewPanelHandle, visible: boolean): void; + $disposeWebviewView(webviewHandle: WebviewPanelHandle): void; } export enum CellKind { diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index f005de99781..cdeb6495e1f 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -267,6 +267,92 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa } } +export class ExtHostWebviewView extends Disposable implements vscode.WebviewView { + + readonly #handle: extHostProtocol.WebviewPanelHandle; + readonly #proxy: extHostProtocol.MainThreadWebviewsShape; + + readonly #viewType: string; + readonly #webview: ExtHostWebview; + + #isDisposed = false; + #isVisible: boolean; + #title: string | undefined; + + constructor( + handle: extHostProtocol.WebviewPanelHandle, + proxy: extHostProtocol.MainThreadWebviewsShape, + viewType: string, + webview: ExtHostWebview, + isVisible: boolean, + ) { + super(); + + this.#viewType = viewType; + this.#handle = handle; + this.#proxy = proxy; + this.#webview = webview; + this.#isVisible = isVisible; + } + + public dispose() { + if (this.#isDisposed) { + return; + } + + this.#isDisposed = true; + this.#onDidDispose.fire(); + + super.dispose(); + } + + readonly #onDidChangeVisibility = this._register(new Emitter()); + public readonly onDidChangeVisibility = this.#onDidChangeVisibility.event; + + readonly #onDidDispose = this._register(new Emitter()); + public readonly onDidDispose = this.#onDidDispose.event; + + get title(): string | undefined { + this.assertNotDisposed(); + return this.#title; + } + + set title(value: string | undefined) { + this.assertNotDisposed(); + if (this.#title !== value) { + this.#title = value; + this.#proxy.$setWebviewViewTitle(this.#handle, value); + } + } + + get visible() { + return this.#isVisible; + } + + get webview() { + return this.#webview; + } + + get viewType(): string { + return this.#viewType; + } + + _setVisible(visible: boolean) { + if (visible === this.#isVisible) { + return; + } + + this.#isVisible = visible; + this.#onDidChangeVisibility.fire(); + } + + private assertNotDisposed() { + if (this.#isDisposed) { + throw new Error('Webview is disposed'); + } + } +} + class CustomDocumentStoreEntry { private _backupCounter = 1; @@ -412,7 +498,13 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { readonly extension: IExtensionDescription; }>(); + private readonly _viewProviders = new Map(); + private readonly _editorProviders = new EditorProviderStore(); + private readonly _webviewViews = new Map(); private readonly _documents = new CustomDocumentStore(); @@ -468,6 +560,27 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { }); } + public registerWebviewViewProvider( + extension: IExtensionDescription, + viewType: string, + provider: vscode.WebviewViewProvider, + webviewOptions?: { + retainContextWhenHidden?: boolean + }, + ): vscode.Disposable { + if (this._viewProviders.has(viewType)) { + throw new Error(`View provider for '${viewType}' already registered`); + } + + this._viewProviders.set(viewType, { provider, extension }); + this._proxy.$registerWebviewViewProvider(viewType, webviewOptions); + + return new extHostTypes.Disposable(() => { + this._viewProviders.delete(viewType); + this._proxy.$unregisterSerializer(viewType); + }); + } + public registerCustomEditorProvider( extension: IExtensionDescription, viewType: string, @@ -583,6 +696,41 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { await serializer.deserializeWebviewPanel(revivedPanel, state); } + async $resolveWebviewView( + webviewHandle: string, + viewType: string, + state: any, + cancellation: CancellationToken, + ): Promise { + const entry = this._viewProviders.get(viewType); + if (!entry) { + throw new Error(`No view provider found for '${viewType}'`); + } + + const { provider, extension } = entry; + + const webview = new ExtHostWebview(webviewHandle, this._proxy, reviveOptions({ /* todo */ }), this.initData, this.workspace, extension, this._deprecationService); + const revivedView = new ExtHostWebviewView(webviewHandle, this._proxy, viewType, webview, true); + + this._webviewViews.set(webviewHandle, revivedView); + + await provider.resolveWebviewView(revivedView, { state }, cancellation); + } + + async $onDidChangeWebviewViewVisibility( + webviewHandle: string, + visible: boolean + ) { + const webviewView = this.getWebviewView(webviewHandle); + webviewView._setVisible(visible); + } + + async $disposeWebviewView(webviewHandle: string) { + const webviewView = this.getWebviewView(webviewHandle); + this._webviewViews.delete(webviewHandle); + webviewView.dispose(); + } + async $createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken) { const entry = this._editorProviders.get(viewType); if (!entry) { @@ -746,6 +894,14 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { return provider; } + private getWebviewView(handle: string): ExtHostWebviewView { + const entry = this._webviewViews.get(handle); + if (!entry) { + throw new Error('Custom document is not editable'); + } + return entry; + } + private supportEditing( provider: vscode.CustomTextEditorProvider | vscode.CustomEditorProvider | vscode.CustomReadonlyEditorProvider ): provider is vscode.CustomEditorProvider { diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 3d327ef6f64..e50bd99f313 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -196,6 +196,8 @@ Registry.add(Extensions.ViewContainersRegistry, new ViewContainersRegistryImpl() export interface IViewDescriptor { + readonly type?: string; + readonly id: string; readonly name: string; diff --git a/src/vs/workbench/contrib/webviewView/browser/webviewView.contribution.ts b/src/vs/workbench/contrib/webviewView/browser/webviewView.contribution.ts new file mode 100644 index 00000000000..9dd05114160 --- /dev/null +++ b/src/vs/workbench/contrib/webviewView/browser/webviewView.contribution.ts @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IWebviewViewService, WebviewViewService } from 'vs/workbench/contrib/webviewView/browser/webviewViewService'; + +registerSingleton(IWebviewViewService, WebviewViewService, true); diff --git a/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts b/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts new file mode 100644 index 00000000000..d1c1d95b238 --- /dev/null +++ b/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.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 { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { Emitter } from 'vs/base/common/event'; +import { toDisposable } from 'vs/base/common/lifecycle'; +import { setImmediate } from 'vs/base/common/platform'; +import { generateUuid } from 'vs/base/common/uuid'; +import { MenuId } from 'vs/platform/actions/common/actions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IProgressService } from 'vs/platform/progress/common/progress'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; +import { IViewDescriptorService } from 'vs/workbench/common/views'; +import { IWebviewService, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; +import { IWebviewViewService } from 'vs/workbench/contrib/webviewView/browser/webviewViewService'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; + + +declare const ResizeObserver: any; + +export class WebviewViewPane extends ViewPane { + + private _webview?: WebviewOverlay; + private _activated = false; + + private _container?: HTMLElement; + private _resizeObserver?: any; + + constructor( + options: IViewletViewOptions, + @IKeybindingService keybindingService: IKeybindingService, + @IContextMenuService contextMenuService: IContextMenuService, + @IConfigurationService configurationService: IConfigurationService, + @IContextKeyService contextKeyService: IContextKeyService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @IInstantiationService instantiationService: IInstantiationService, + @IOpenerService openerService: IOpenerService, + @IThemeService themeService: IThemeService, + @ITelemetryService telemetryService: ITelemetryService, + @IExtensionService private readonly extensionService: IExtensionService, + @IProgressService private readonly progressService: IProgressService, + @IWebviewService private readonly webviewService: IWebviewService, + @IWebviewViewService private readonly webviewViewService: IWebviewViewService, + ) { + super({ ...options, titleMenuId: MenuId.ViewTitle }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); + + this._register(this.onDidChangeBodyVisibility(() => this.updateTreeVisibility())); + this.updateTreeVisibility(); + } + + private readonly _onDidChangeVisibility = this._register(new Emitter()); + readonly onDidChangeVisibility = this._onDidChangeVisibility.event; + + private readonly _onDispose = this._register(new Emitter()); + readonly onDispose = this._onDispose.event; + + dispose() { + this._onDispose.fire(); + + super.dispose(); + } + + focus(): void { + super.focus(); + this._webview?.focus(); + } + + renderBody(container: HTMLElement): void { + super.renderBody(container); + + this._container = container; + + if (!this._resizeObserver) { + this._resizeObserver = new ResizeObserver(() => { + setImmediate(() => { + if (this._container) { + this._webview?.layoutWebviewOverElement(this._container); + } + }); + }); + + this._register(toDisposable(() => { + this._resizeObserver.disconnect(); + })); + this._resizeObserver.observe(container); + } + } + + protected layoutBody(height: number, width: number): void { + super.layoutBody(height, width); + + if (!this._webview) { + return; + } + + if (this._container) { + this._webview.layoutWebviewOverElement(this._container, { width, height }); + } + } + + private updateTreeVisibility() { + if (this.isBodyVisible()) { + this.activate(); + this._webview?.claim(this); + } else { + this._webview?.release(this); + } + } + + private activate() { + if (!this._activated) { + this._activated = true; + + const webview = this.webviewService.createWebviewOverlay(generateUuid(), {}, {}, undefined); + this._webview = webview; + + this._register(toDisposable(() => { + this._webview?.release(this); + })); + + const source = this._register(new CancellationTokenSource()); + + this.withProgress(async () => { + await this.extensionService.activateByEvent(`onView:${this.id}`); + + let self = this; + await this.webviewViewService.resolve(this.id, { + webview, + onDidChangeVisibility: this.onDidChangeBodyVisibility, + onDispose: this.onDispose, + get title() { return self.title; }, + set title(value: string) { self.updateTitle(value); } + }, source.token); + }); + } + } + + private async withProgress(task: () => Promise): Promise { + return this.progressService.withProgress({ location: this.id, delay: 500 }, task); + } +} + + diff --git a/src/vs/workbench/contrib/webviewView/browser/webviewViewService.ts b/src/vs/workbench/contrib/webviewView/browser/webviewViewService.ts new file mode 100644 index 00000000000..5de370ac42d --- /dev/null +++ b/src/vs/workbench/contrib/webviewView/browser/webviewViewService.ts @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Event } from 'vs/base/common/event'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; + +export const IWebviewViewService = createDecorator('webviewViewService'); + +export interface WebviewView { + title?: string; + + readonly webview: WebviewOverlay; + + readonly onDidChangeVisibility: Event; + readonly onDispose: Event; +} + +export interface IWebviewViewResolver { + resolve(webviewView: WebviewView, cancellation: CancellationToken): Promise; +} + +export interface IWebviewViewService { + + readonly _serviceBrand: undefined; + + register(type: string, resolver: IWebviewViewResolver): IDisposable; + + resolve(viewType: string, webview: WebviewView, cancellation: CancellationToken): Promise; +} + +export class WebviewViewService extends Disposable implements IWebviewViewService { + + readonly _serviceBrand: undefined; + + private readonly _views = new Map(); + + private readonly _awaitingRevival = new Map void }>(); + + constructor() { + super(); + } + + register(viewType: string, resolver: IWebviewViewResolver): IDisposable { + if (this._views.has(viewType)) { + throw new Error(`View resolver already registered for ${viewType}`); + } + + this._views.set(viewType, resolver); + + const pending = this._awaitingRevival.get(viewType); + if (pending) { + resolver.resolve(pending.webview, CancellationToken.None).then(() => { + this._awaitingRevival.delete(viewType); + pending.resolve(); + }); + } + + return toDisposable(() => { + this._views.delete(viewType); + }); + } + + resolve(viewType: string, webview: WebviewView, cancellation: CancellationToken): Promise { + const resolver = this._views.get(viewType); + if (!resolver) { + if (this._awaitingRevival.has(viewType)) { + throw new Error('View already awaiting revival'); + } + + let resolve: () => void; + const p = new Promise(r => resolve = r); + this._awaitingRevival.set(viewType, { webview, resolve: resolve! }); + return p; + } + + return resolver.resolve(webview, cancellation); + } +} + diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 6d6d66e1381..4a06e4a5ffc 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -199,6 +199,7 @@ import 'vs/workbench/contrib/url/browser/url.contribution'; // Webview import 'vs/workbench/contrib/webview/browser/webview.contribution'; +import 'vs/workbench/contrib/webviewView/browser/webviewView.contribution'; import 'vs/workbench/contrib/customEditor/browser/customEditor.contribution'; // Extensions Management From b0c6e84bee616b266a979a95019513c3992e87d3 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 20 Aug 2020 14:51:36 -0700 Subject: [PATCH 387/736] Increase hover range for insertion toolbar Fix #104854 --- .../contrib/notebook/browser/constants.ts | 4 ++-- .../contrib/notebook/browser/media/notebook.css | 1 + .../notebook/browser/notebookEditorWidget.ts | 6 ++++-- .../browser/view/renderers/cellRenderer.ts | 6 +++--- .../contrib/notebook/browser/view/renderers/dnd.ts | 6 +++--- .../browser/viewModel/codeCellViewModel.ts | 10 +++++----- .../browser/viewModel/markdownCellViewModel.ts | 14 +++++++------- 7 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/constants.ts b/src/vs/workbench/contrib/notebook/browser/constants.ts index b6b383674be..b77127d1392 100644 --- a/src/vs/workbench/contrib/notebook/browser/constants.ts +++ b/src/vs/workbench/contrib/notebook/browser/constants.ts @@ -13,8 +13,8 @@ export const CELL_RUN_GUTTER = 28; export const CODE_CELL_LEFT_MARGIN = 32; export const EDITOR_TOOLBAR_HEIGHT = 0; -export const BOTTOM_CELL_TOOLBAR_HEIGHT = 18; -export const BOTTOM_CELL_TOOLBAR_OFFSET = 12; +export const BOTTOM_CELL_TOOLBAR_GAP = 18; +export const BOTTOM_CELL_TOOLBAR_HEIGHT = 50; export const CELL_STATUSBAR_HEIGHT = 22; // Margin above editor diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index b8d061f818b..560a756fe12 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -522,6 +522,7 @@ .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container { position: absolute; display: flex; + align-items: center; justify-content: center; z-index: 25; /* over the focus outline on the editor, below the title toolbar */ width: 100%; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 378c52ad7c9..2f6ef03be4d 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -26,7 +26,7 @@ import { contrastBorder, editorBackground, focusBorder, foreground, registerColo import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { EditorMemento } from 'vs/workbench/browser/parts/editor/baseEditor'; import { IEditorMemento } from 'vs/workbench/common/editor'; -import { CELL_MARGIN, CELL_RUN_GUTTER, CELL_TOP_MARGIN, SCROLLABLE_ELEMENT_PADDING_TOP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; +import { CELL_MARGIN, CELL_RUN_GUTTER, CELL_TOP_MARGIN, SCROLLABLE_ELEMENT_PADDING_TOP, BOTTOM_CELL_TOOLBAR_GAP, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, BOTTOM_CELL_TOOLBAR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; import { CellEditState, CellFocusMode, ICellRange, ICellViewModel, INotebookCellList, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_OUTPUT_FOCUSED, INotebookDeltaDecoration, NotebookEditorOptions, INotebookEditorWidgetOptions, INotebookEditorContributionDescription } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList'; @@ -1937,7 +1937,7 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell.markdown { padding-left: ${CELL_RUN_GUTTER}px; }`); collector.addRule(`.notebookOverlay .cell .run-button-container { width: 20px; margin: 0px ${Math.floor(CELL_RUN_GUTTER - 20) / 2}px; }`); collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-top { height: ${CELL_TOP_MARGIN}px; }`); - collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-side { bottom: ${BOTTOM_CELL_TOOLBAR_HEIGHT}px; }`); + collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-side { bottom: ${BOTTOM_CELL_TOOLBAR_GAP}px; }`); collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row.code-cell-row .cell-focus-indicator-left, .notebookOverlay .monaco-list .monaco-list-row.code-cell-row .cell-drag-handle { width: ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`); collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row .cell-focus-indicator-left { width: ${CODE_CELL_LEFT_MARGIN}px; }`); @@ -1947,4 +1947,6 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-collapsed-part { margin-left: ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; height: ${COLLAPSED_INDICATOR_HEIGHT}px; }`); collector.addRule(`.notebookOverlay .cell-list-top-cell-toolbar-container { top: -${SCROLLABLE_ELEMENT_PADDING_TOP}px }`); + + collector.addRule(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container { height: ${BOTTOM_CELL_TOOLBAR_HEIGHT}px }`); }); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index f2195abb858..574f99c36fc 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -35,7 +35,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_TOP_MARGIN, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; +import { BOTTOM_CELL_TOOLBAR_GAP, CELL_BOTTOM_MARGIN, CELL_TOP_MARGIN, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; import { CancelCellAction, DeleteCellAction, ExecuteCellAction, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; import { BaseCellRenderTemplate, CellEditState, CodeCellRenderTemplate, EXPAND_CELL_CONTENT_COMMAND_ID, ICellViewModel, INotebookEditor, isCodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys'; @@ -805,9 +805,9 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende private updateForLayout(element: CodeCellViewModel, templateData: CodeCellRenderTemplate): void { templateData.focusIndicatorLeft.style.height = `${element.layoutInfo.indicatorHeight}px`; templateData.focusIndicatorRight.style.height = `${element.layoutInfo.indicatorHeight}px`; - templateData.focusIndicatorBottom.style.top = `${element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_HEIGHT - CELL_BOTTOM_MARGIN}px`; + templateData.focusIndicatorBottom.style.top = `${element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_GAP - CELL_BOTTOM_MARGIN}px`; templateData.outputContainer.style.top = `${element.layoutInfo.outputContainerOffset}px`; - templateData.dragHandle.style.height = `${element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_HEIGHT}px`; + templateData.dragHandle.style.height = `${element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_GAP}px`; } renderElement(element: CodeCellViewModel, index: number, templateData: CodeCellRenderTemplate, height: number | undefined): void { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/dnd.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/dnd.ts index 57352dd8151..7f5e4ad30a0 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/dnd.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/dnd.ts @@ -9,7 +9,7 @@ import { domEvent } from 'vs/base/browser/event'; import { Delayer } from 'vs/base/common/async'; import { Disposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; -import { BOTTOM_CELL_TOOLBAR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; +import { BOTTOM_CELL_TOOLBAR_GAP } from 'vs/workbench/contrib/notebook/browser/constants'; import { BaseCellRenderTemplate, CellEditState, ICellViewModel, INotebookCellList, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -151,7 +151,7 @@ export class CellDragAndDropController extends Disposable { const dropDirection = this.getDropInsertDirection(event); const insertionIndicatorAbsolutePos = dropDirection === 'above' ? event.cellTop : event.cellTop + event.cellHeight; - const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + BOTTOM_CELL_TOOLBAR_HEIGHT / 2; + const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + BOTTOM_CELL_TOOLBAR_GAP / 2; if (insertionIndicatorTop >= 0) { this.listInsertionIndicator.style.top = `${insertionIndicatorTop}px`; this.setInsertIndicatorVisibility(true); @@ -191,7 +191,7 @@ export class CellDragAndDropController extends Disposable { const dropDirection = this.getDropInsertDirection(event); const insertionIndicatorAbsolutePos = dropDirection === 'above' ? event.cellTop : event.cellTop + event.cellHeight; - const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + BOTTOM_CELL_TOOLBAR_HEIGHT / 2; + const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + BOTTOM_CELL_TOOLBAR_GAP / 2; const editorHeight = this.notebookEditor.getDomNode().getBoundingClientRect().height; if (insertionIndicatorTop < 0 || insertionIndicatorTop > editorHeight) { // Ignore drop, insertion point is off-screen diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts index 306c9722c8d..cca77b7d78e 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts @@ -8,7 +8,7 @@ import * as UUID from 'vs/base/common/uuid'; import * as editorCommon from 'vs/editor/common/editorCommon'; import * as model from 'vs/editor/common/model'; import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; -import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_MARGIN, CELL_RUN_GUTTER, CELL_STATUSBAR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, CELL_TOP_MARGIN, EDITOR_TOP_PADDING, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN, BOTTOM_CELL_TOOLBAR_OFFSET, COLLAPSED_INDICATOR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; +import { BOTTOM_CELL_TOOLBAR_GAP, CELL_MARGIN, CELL_RUN_GUTTER, CELL_STATUSBAR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, CELL_TOP_MARGIN, EDITOR_TOP_PADDING, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN, BOTTOM_CELL_TOOLBAR_HEIGHT, COLLAPSED_INDICATOR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; import { CellEditState, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, ICellViewModel, NotebookLayoutInfo, CodeCellLayoutState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { CellKind, NotebookCellOutputsSplice, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -123,7 +123,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod const indicatorHeight = editorHeight + CELL_STATUSBAR_HEIGHT + outputTotalHeight; const outputContainerOffset = EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + CELL_STATUSBAR_HEIGHT; - const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_HEIGHT - BOTTOM_CELL_TOOLBAR_OFFSET; + const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2; const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo?.editorWidth; this._layoutInfo = { @@ -141,8 +141,8 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod outputTotalHeight = this.metadata?.inputCollapsed && this.metadata.outputCollapsed ? 0 : outputTotalHeight; const indicatorHeight = COLLAPSED_INDICATOR_HEIGHT + outputTotalHeight; const outputContainerOffset = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT; - const totalHeight = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT + CELL_BOTTOM_MARGIN + BOTTOM_CELL_TOOLBAR_HEIGHT + outputTotalHeight; - const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_HEIGHT - BOTTOM_CELL_TOOLBAR_OFFSET; + const totalHeight = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT + CELL_BOTTOM_MARGIN + BOTTOM_CELL_TOOLBAR_GAP + outputTotalHeight; + const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2; const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo?.editorWidth; this._layoutInfo = { @@ -209,7 +209,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod } private computeTotalHeight(editorHeight: number, outputsTotalHeight: number): number { - return EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + CELL_STATUSBAR_HEIGHT + outputsTotalHeight + BOTTOM_CELL_TOOLBAR_HEIGHT + CELL_BOTTOM_MARGIN; + return EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + CELL_STATUSBAR_HEIGHT + outputsTotalHeight + BOTTOM_CELL_TOOLBAR_GAP + CELL_BOTTOM_MARGIN; } /** diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts index 9c1dc125163..86f9720eac8 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts @@ -8,7 +8,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import * as UUID from 'vs/base/common/uuid'; import * as editorCommon from 'vs/editor/common/editorCommon'; import * as model from 'vs/editor/common/model'; -import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_MARGIN, CELL_STATUSBAR_HEIGHT, CELL_TOP_MARGIN, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN, BOTTOM_CELL_TOOLBAR_OFFSET, COLLAPSED_INDICATOR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; +import { BOTTOM_CELL_TOOLBAR_GAP, CELL_MARGIN, CELL_STATUSBAR_HEIGHT, CELL_TOP_MARGIN, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN, BOTTOM_CELL_TOOLBAR_HEIGHT, COLLAPSED_INDICATOR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; import { CellFindMatch, ICellViewModel, MarkdownCellLayoutChangeEvent, MarkdownCellLayoutInfo, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { MarkdownRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/mdRenderer'; import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel'; @@ -27,7 +27,7 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie } set renderedMarkdownHeight(newHeight: number) { - const newTotalHeight = newHeight + BOTTOM_CELL_TOOLBAR_HEIGHT; + const newTotalHeight = newHeight + BOTTOM_CELL_TOOLBAR_GAP; this.totalHeight = newTotalHeight; } @@ -45,7 +45,7 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie set editorHeight(newHeight: number) { this._editorHeight = newHeight; - this.totalHeight = this._editorHeight + CELL_TOP_MARGIN + CELL_BOTTOM_MARGIN + BOTTOM_CELL_TOOLBAR_HEIGHT + CELL_STATUSBAR_HEIGHT; + this.totalHeight = this._editorHeight + CELL_TOP_MARGIN + CELL_BOTTOM_MARGIN + BOTTOM_CELL_TOOLBAR_GAP + CELL_STATUSBAR_HEIGHT; } get editorHeight() { @@ -73,7 +73,7 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie editorHeight: 0, fontInfo: initialNotebookLayoutInfo?.fontInfo || null, editorWidth: initialNotebookLayoutInfo?.width ? this.computeEditorWidth(initialNotebookLayoutInfo.width) : 0, - bottomToolbarOffset: BOTTOM_CELL_TOOLBAR_HEIGHT, + bottomToolbarOffset: BOTTOM_CELL_TOOLBAR_GAP, totalHeight: 0 }; @@ -101,19 +101,19 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie fontInfo: state.font || this._layoutInfo.fontInfo, editorWidth, editorHeight: this._editorHeight, - bottomToolbarOffset: totalHeight - BOTTOM_CELL_TOOLBAR_HEIGHT - BOTTOM_CELL_TOOLBAR_OFFSET, + bottomToolbarOffset: totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2, totalHeight }; } else { const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo.editorWidth; - const totalHeight = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT + BOTTOM_CELL_TOOLBAR_HEIGHT + CELL_BOTTOM_MARGIN; + const totalHeight = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT + BOTTOM_CELL_TOOLBAR_GAP + CELL_BOTTOM_MARGIN; state.totalHeight = totalHeight; this._layoutInfo = { fontInfo: state.font || this._layoutInfo.fontInfo, editorWidth, editorHeight: this._editorHeight, - bottomToolbarOffset: totalHeight - BOTTOM_CELL_TOOLBAR_HEIGHT - BOTTOM_CELL_TOOLBAR_OFFSET, + bottomToolbarOffset: totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2, totalHeight }; } From dc0150c61aa8221bb955065d9abe59fa1e6dd87f Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 20 Aug 2020 15:44:59 -0700 Subject: [PATCH 388/736] debug: make auto attach apply state transition in new workspaces Previous there was a case: 1. Auto attach was enabled in user settings and workspace A was open 2. Switch to workspace B, and then turn auto attach off in user settings 3. Switching back to workspace A, environment variables were not cleared Now, the last state is stored in the workspace settings so that we can tear down the previous state if necessary. --- .../debug-auto-launch/.vscode/launch.json | 20 ++++++ extensions/debug-auto-launch/src/extension.ts | 61 ++++++++++++------- 2 files changed, 58 insertions(+), 23 deletions(-) create mode 100644 extensions/debug-auto-launch/.vscode/launch.json diff --git a/extensions/debug-auto-launch/.vscode/launch.json b/extensions/debug-auto-launch/.vscode/launch.json new file mode 100644 index 00000000000..1c3d9e9661b --- /dev/null +++ b/extensions/debug-auto-launch/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Extension", + "type": "extensionHost", + "request": "launch", + "skipFiles": ["/**"], + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}", + ], + "outFiles": [ + "${workspaceFolder}/out/**/*.js", + ], + } + ] +} diff --git a/extensions/debug-auto-launch/src/extension.ts b/extensions/debug-auto-launch/src/extension.ts index 4148e1843fe..37be929aed8 100644 --- a/extensions/debug-auto-launch/src/extension.ts +++ b/extensions/debug-auto-launch/src/extension.ts @@ -17,6 +17,7 @@ const JS_DEBUG_USEPREVIEWAA = 'usePreviewAutoAttach'; const JS_DEBUG_IPC_KEY = 'jsDebugIpcState'; const NODE_DEBUG_SETTINGS = 'debug.node'; const AUTO_ATTACH_SETTING = 'autoAttach'; +const LAST_STATE_STORAGE_KEY = 'lastState'; type AUTO_ATTACH_VALUES = 'disabled' | 'on' | 'off'; @@ -28,10 +29,14 @@ const enum State { } // on activation this feature is always disabled... -let currentState = Promise.resolve({ state: State.Disabled, transitionData: null as unknown }); +let currentState: Promise<{ context: vscode.ExtensionContext, state: State; transitionData: unknown }>; let statusItem: vscode.StatusBarItem | undefined; // and there is no status bar item export function activate(context: vscode.ExtensionContext): void { + const previousState = context.workspaceState.get(LAST_STATE_STORAGE_KEY, State.Disabled); + currentState = Promise.resolve(transitions[previousState].onActivate?.(context, readCurrentState())) + .then(() => ({ context, state: State.Disabled, transitionData: null })); + context.subscriptions.push(vscode.commands.registerCommand(TOGGLE_COMMAND, toggleAutoAttachSetting)); // settings that can result in the "state" being changed--on/off/disable or useV3 toggles @@ -43,17 +48,17 @@ export function activate(context: vscode.ExtensionContext): void { context.subscriptions.push( vscode.workspace.onDidChangeConfiguration((e) => { if (effectualConfigurationSettings.some(setting => e.affectsConfiguration(setting))) { - updateAutoAttach(context); + updateAutoAttach(); } }) ); - updateAutoAttach(context); + updateAutoAttach(); } export async function deactivate(): Promise { - const { state, transitionData } = await currentState; - await transitions[state].exit?.(transitionData); + const { context, state, transitionData } = await currentState; + await transitions[state].exit?.(context, transitionData); } function toggleAutoAttachSetting() { @@ -136,30 +141,33 @@ interface CachedIpcState { } interface StateTransition { - exit?(stateData: StateData): Promise | void; + onActivate?(context: vscode.ExtensionContext, currentState: State): Promise; + exit?(context: vscode.ExtensionContext, stateData: StateData): Promise | void; enter?(context: vscode.ExtensionContext): Promise | StateData; } +const makeTransition = (tsn: StateTransition) => tsn; // helper to apply generic type + /** * Map of logic that happens when auto attach states are entered and exited. * All state transitions are queued and run in order; promises are awaited. */ const transitions: { [S in State]: StateTransition } = { - [State.Disabled]: { + [State.Disabled]: makeTransition({ async enter(context) { statusItem?.hide(); await clearJsDebugAttachState(context); }, - }, + }), - [State.Off]: { + [State.Off]: makeTransition({ enter(context) { const statusItem = ensureStatusBarExists(context); statusItem.text = OFF_TEXT; }, - }, + }), - [State.OnWithNodeDebug]: { + [State.OnWithNodeDebug]: makeTransition({ async enter(context) { const statusItem = ensureStatusBarExists(context); const vscode_pid = process.env['VSCODE_PID']; @@ -171,16 +179,16 @@ const transitions: { [S in State]: StateTransition } = { async exit() { await vscode.commands.executeCommand('extension.node-debug.stopAutoAttach'); }, - }, + }), - [State.OnWithJsDebug]: { + [State.OnWithJsDebug]: makeTransition({ async enter(context) { const ipcAddress = await getIpcAddress(context); if (!ipcAddress) { - return { context }; + return null; } - const server = await new Promise((resolve, reject) => { + const server = await new Promise((resolve, reject) => { const s = createServer((socket) => { let data: Buffer[] = []; socket.on('data', (chunk) => data.push(chunk)); @@ -201,10 +209,10 @@ const transitions: { [S in State]: StateTransition } = { const statusItem = ensureStatusBarExists(context); statusItem.text = ON_TEXT; - return { server, context }; + return server || null; }, - async exit({ server, context }: { server?: Server, context: vscode.ExtensionContext }) { + async exit(context, server) { // we don't need to clear the environment variables--the bootloader will // no-op if the debug server is closed. This prevents having to reload // terminals if users want to turn it back on. @@ -217,24 +225,31 @@ const transitions: { [S in State]: StateTransition } = { await clearJsDebugAttachState(context); } }, - }, + + async onActivate(context, currentState) { + if (currentState === State.OnWithNodeDebug || currentState === State.Disabled) { + await clearJsDebugAttachState(context); + } + } + }), }; /** * Updates the auto attach feature based on the user or workspace setting */ -function updateAutoAttach(context: vscode.ExtensionContext) { +function updateAutoAttach() { const newState = readCurrentState(); - currentState = currentState.then(async ({ state: oldState, transitionData }) => { + currentState = currentState.then(async ({ context, state: oldState, transitionData }) => { if (newState === oldState) { - return { state: oldState, transitionData }; + return { context, state: oldState, transitionData }; } - await transitions[oldState].exit?.(transitionData); + await transitions[oldState].exit?.(context, transitionData); const newData = await transitions[newState].enter?.(context); + await context.workspaceState.update(LAST_STATE_STORAGE_KEY, newState); - return { state: newState, transitionData: newData }; + return { context, state: newState, transitionData: newData }; }); } From 4fd7f660a4c4b005f7bc400f27f51a5680c30016 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 20 Aug 2020 15:19:14 -0700 Subject: [PATCH 389/736] Move webview views into own ext host class Also fixes message passing for webview views --- .../api/browser/mainThreadWebview.ts | 26 ++- .../workbench/api/common/extHost.api.impl.ts | 4 +- .../workbench/api/common/extHost.protocol.ts | 6 + src/vs/workbench/api/common/extHostWebview.ts | 198 +++--------------- .../api/common/extHostWebviewView.ts | 176 ++++++++++++++++ 5 files changed, 236 insertions(+), 174 deletions(-) create mode 100644 src/vs/workbench/api/common/extHostWebviewView.ts diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index be1f4ed4cbc..e1a231fc36f 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -118,6 +118,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma ]); private readonly _proxy: extHostProtocol.ExtHostWebviewsShape; + private readonly _viewsProxy: extHostProtocol.ExtHostWebviewViewsShape; private readonly _webviewInputs = new WebviewInputStore(); private readonly _revivers = new Map(); @@ -146,6 +147,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma super(); this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviews); + this._viewsProxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewViews); this._register(_editorService.onDidActiveEditorChange(() => { const activeInput = this._editorService.activeEditor; @@ -259,8 +261,8 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma } public $setOptions(handle: extHostProtocol.WebviewPanelHandle, options: modes.IWebviewOptions): void { - const webview = this.getWebviewInput(handle); - webview.webview.contentOptions = reviveWebviewOptions(options); + const webview = this.getWebview(handle); + webview.contentOptions = reviveWebviewOptions(options); } public $reveal(handle: extHostProtocol.WebviewPanelHandle, showOptions: extHostProtocol.WebviewPanelShowOptions): void { @@ -276,8 +278,8 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma } public async $postMessage(handle: extHostProtocol.WebviewPanelHandle, message: any): Promise { - const webview = this.getWebviewInput(handle); - webview.webview.postMessage(message); + const webview = this.getWebview(handle); + webview.postMessage(message); return true; } @@ -356,15 +358,15 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma } webviewView.onDidChangeVisibility(visible => { - this._proxy.$onDidChangeWebviewViewVisibility(handle, visible); + this._viewsProxy.$onDidChangeWebviewViewVisibility(handle, visible); }); webviewView.onDispose(() => { - this._proxy.$disposeWebviewView(handle); + this._viewsProxy.$disposeWebviewView(handle); }); try { - await this._proxy.$resolveWebviewView(handle, viewType, state, cancellation); + await this._viewsProxy.$resolveWebviewView(handle, viewType, state, cancellation); } catch (error) { onUnexpectedError(error); webviewView.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(viewType); @@ -373,6 +375,16 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma }); } + public $unregisterWebviewViewProvider(viewType: string): void { + const provider = this._webviewViewProviders.get(viewType); + if (!provider) { + throw new Error(`No view provider for ${viewType} registered`); + } + + provider.dispose(); + this._webviewViewProviders.delete(viewType); + } + public $registerTextEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: extHostProtocol.CustomTextEditorCapabilities): void { this.registerEditorProvider(ModelType.Text, extensionData, viewType, options, capabilities, true); } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 25aa14724b4..d36555cf30e 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -76,6 +76,7 @@ import { ExtHostTimeline } from 'vs/workbench/api/common/extHostTimeline'; import { ExtHostNotebookConcatDocument } from 'vs/workbench/api/common/extHostNotebookConcatDocument'; import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer'; +import { ExtHostWebviewViews } from 'vs/workbench/api/common/extHostWebviewView'; export interface IExtensionApiFactory { (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; @@ -142,6 +143,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol)); const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol, extHostCommands)); const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace, extHostLogService, extHostApiDeprecation, extHostDocuments, extensionStoragePaths)); + const extHostWebviewViews = rpcProtocol.set(ExtHostContext.ExtHostWebviewViews, new ExtHostWebviewViews(rpcProtocol, extHostWebviews)); // Check that no named customers are missing const expected: ProxyIdentifier[] = values(ExtHostContext); @@ -619,7 +621,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I } }) { checkProposedApiEnabled(extension); - return extHostWebviews.registerWebviewViewProvider(extension, viewId, provider, options?.webviewOptions); + return extHostWebviewViews.registerWebviewViewProvider(extension, viewId, provider, options?.webviewOptions); } }; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 29c9ebd1f0f..2546199fcc3 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -631,6 +631,7 @@ export interface MainThreadWebviewsShape extends IDisposable { $onContentChange(resource: UriComponents, viewType: string): void; $registerWebviewViewProvider(viewType: string, options?: { retainContextWhenHidden?: boolean }): void; + $unregisterWebviewViewProvider(viewType: string): void; $setWebviewViewTitle(handle: WebviewPanelHandle, value: string | undefined): void; } @@ -666,9 +667,13 @@ export interface ExtHostWebviewsShape { $backup(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise; $onMoveCustomEditor(handle: WebviewPanelHandle, newResource: UriComponents, viewType: string): Promise; +} +export interface ExtHostWebviewViewsShape { $resolveWebviewView(webviewHandle: WebviewPanelHandle, viewType: string, state: any, cancellation: CancellationToken): Promise; + $onDidChangeWebviewViewVisibility(webviewHandle: WebviewPanelHandle, visible: boolean): void; + $disposeWebviewView(webviewHandle: WebviewPanelHandle): void; } @@ -1744,6 +1749,7 @@ export const ExtHostContext = { ExtHostWorkspace: createExtId('ExtHostWorkspace'), ExtHostWindow: createExtId('ExtHostWindow'), ExtHostWebviews: createExtId('ExtHostWebviews'), + ExtHostWebviewViews: createExtId('ExtHostWebviewViews'), ExtHostEditorInsets: createExtId('ExtHostEditorInsets'), ExtHostProgress: createMainId('ExtHostProgress'), ExtHostComments: createMainId('ExtHostComments'), diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index cdeb6495e1f..2e74c83c3cf 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -64,7 +64,13 @@ export class ExtHostWebview implements vscode.Webview { /* internal */ readonly _onMessageEmitter = new Emitter(); public readonly onDidReceiveMessage: Event = this._onMessageEmitter.event; + readonly #onDidDisposeEmitter = new Emitter(); + /* internal */ readonly _onDidDispose: Event = this.#onDidDisposeEmitter.event; + public dispose() { + this.#onDidDisposeEmitter.fire(); + + this.#onDidDisposeEmitter.dispose(); this._onMessageEmitter.dispose(); } @@ -167,6 +173,7 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa this.#isDisposed = true; this.#onDidDispose.fire(); + this.#proxy.$disposeWebview(this.#handle); this.#webview.dispose(); @@ -267,92 +274,6 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa } } -export class ExtHostWebviewView extends Disposable implements vscode.WebviewView { - - readonly #handle: extHostProtocol.WebviewPanelHandle; - readonly #proxy: extHostProtocol.MainThreadWebviewsShape; - - readonly #viewType: string; - readonly #webview: ExtHostWebview; - - #isDisposed = false; - #isVisible: boolean; - #title: string | undefined; - - constructor( - handle: extHostProtocol.WebviewPanelHandle, - proxy: extHostProtocol.MainThreadWebviewsShape, - viewType: string, - webview: ExtHostWebview, - isVisible: boolean, - ) { - super(); - - this.#viewType = viewType; - this.#handle = handle; - this.#proxy = proxy; - this.#webview = webview; - this.#isVisible = isVisible; - } - - public dispose() { - if (this.#isDisposed) { - return; - } - - this.#isDisposed = true; - this.#onDidDispose.fire(); - - super.dispose(); - } - - readonly #onDidChangeVisibility = this._register(new Emitter()); - public readonly onDidChangeVisibility = this.#onDidChangeVisibility.event; - - readonly #onDidDispose = this._register(new Emitter()); - public readonly onDidDispose = this.#onDidDispose.event; - - get title(): string | undefined { - this.assertNotDisposed(); - return this.#title; - } - - set title(value: string | undefined) { - this.assertNotDisposed(); - if (this.#title !== value) { - this.#title = value; - this.#proxy.$setWebviewViewTitle(this.#handle, value); - } - } - - get visible() { - return this.#isVisible; - } - - get webview() { - return this.#webview; - } - - get viewType(): string { - return this.#viewType; - } - - _setVisible(visible: boolean) { - if (visible === this.#isVisible) { - return; - } - - this.#isVisible = visible; - this.#onDidChangeVisibility.fire(); - } - - private assertNotDisposed() { - if (this.#isDisposed) { - throw new Error('Webview is disposed'); - } - } -} - class CustomDocumentStoreEntry { private _backupCounter = 1; @@ -491,6 +412,8 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { } private readonly _proxy: extHostProtocol.MainThreadWebviewsShape; + + private readonly _webviews = new Map(); private readonly _webviewPanels = new Map(); private readonly _serializers = new Map(); - private readonly _viewProviders = new Map(); - private readonly _editorProviders = new EditorProviderStore(); - private readonly _webviewViews = new Map(); private readonly _documents = new CustomDocumentStore(); @@ -536,7 +453,7 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { const handle = ExtHostWebviews.newHandle(); this._proxy.$createWebviewPanel(toExtensionData(extension), handle, viewType, title, webviewShowOptions, convertWebviewOptions(extension, this.workspace, options)); - const webview = new ExtHostWebview(handle, this._proxy, options, this.initData, this.workspace, extension, this._deprecationService); + const webview = this.createNewWebview(handle, options, extension); const panel = new ExtHostWebviewEditor(handle, this._proxy, viewType, title, viewColumn, options, webview); this._webviewPanels.set(handle, panel); return panel; @@ -560,27 +477,6 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { }); } - public registerWebviewViewProvider( - extension: IExtensionDescription, - viewType: string, - provider: vscode.WebviewViewProvider, - webviewOptions?: { - retainContextWhenHidden?: boolean - }, - ): vscode.Disposable { - if (this._viewProviders.has(viewType)) { - throw new Error(`View provider for '${viewType}' already registered`); - } - - this._viewProviders.set(viewType, { provider, extension }); - this._proxy.$registerWebviewViewProvider(viewType, webviewOptions); - - return new extHostTypes.Disposable(() => { - this._viewProviders.delete(viewType); - this._proxy.$unregisterSerializer(viewType); - }); - } - public registerCustomEditorProvider( extension: IExtensionDescription, viewType: string, @@ -622,9 +518,9 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { handle: extHostProtocol.WebviewPanelHandle, message: any ): void { - const panel = this.getWebviewPanel(handle); - if (panel) { - panel.webview._onMessageEmitter.fire(message); + const webview = this.getWebview(handle); + if (webview) { + webview._onMessageEmitter.fire(message); } } @@ -670,10 +566,10 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { async $onDidDisposeWebviewPanel(handle: extHostProtocol.WebviewPanelHandle): Promise { const panel = this.getWebviewPanel(handle); - if (panel) { - panel.dispose(); - this._webviewPanels.delete(handle); - } + panel?.dispose(); + + this._webviewPanels.delete(handle); + this._webviews.delete(handle); } async $deserializeWebviewPanel( @@ -690,47 +586,12 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { } const { serializer, extension } = entry; - const webview = new ExtHostWebview(webviewHandle, this._proxy, reviveOptions(options), this.initData, this.workspace, extension, this._deprecationService); + const webview = this.createNewWebview(webviewHandle, options, extension); const revivedPanel = new ExtHostWebviewEditor(webviewHandle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview); this._webviewPanels.set(webviewHandle, revivedPanel); await serializer.deserializeWebviewPanel(revivedPanel, state); } - async $resolveWebviewView( - webviewHandle: string, - viewType: string, - state: any, - cancellation: CancellationToken, - ): Promise { - const entry = this._viewProviders.get(viewType); - if (!entry) { - throw new Error(`No view provider found for '${viewType}'`); - } - - const { provider, extension } = entry; - - const webview = new ExtHostWebview(webviewHandle, this._proxy, reviveOptions({ /* todo */ }), this.initData, this.workspace, extension, this._deprecationService); - const revivedView = new ExtHostWebviewView(webviewHandle, this._proxy, viewType, webview, true); - - this._webviewViews.set(webviewHandle, revivedView); - - await provider.resolveWebviewView(revivedView, { state }, cancellation); - } - - async $onDidChangeWebviewViewVisibility( - webviewHandle: string, - visible: boolean - ) { - const webviewView = this.getWebviewView(webviewHandle); - webviewView._setVisible(visible); - } - - async $disposeWebviewView(webviewHandle: string) { - const webviewView = this.getWebviewView(webviewHandle); - this._webviewViews.delete(webviewHandle); - webviewView.dispose(); - } - async $createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken) { const entry = this._editorProviders.get(viewType); if (!entry) { @@ -783,7 +644,7 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { throw new Error(`No provider found for '${viewType}'`); } - const webview = new ExtHostWebview(handle, this._proxy, reviveOptions(options), this.initData, this.workspace, entry.extension, this._deprecationService); + const webview = this.createNewWebview(handle, options, entry.extension); const revivedPanel = new ExtHostWebviewEditor(handle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview); this._webviewPanels.set(handle, revivedPanel); @@ -873,6 +734,19 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { return backup.id; } + public createNewWebview(handle: string, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, extension: IExtensionDescription): ExtHostWebview { + const webview = new ExtHostWebview(handle, this._proxy, reviveOptions(options), this.initData, this.workspace, extension, this._deprecationService); + this._webviews.set(handle, webview); + + webview._onDidDispose(() => { this._webviews.delete(handle); }); + + return webview; + } + + private getWebview(handle: extHostProtocol.WebviewPanelHandle): ExtHostWebview | undefined { + return this._webviews.get(handle); + } + private getWebviewPanel(handle: extHostProtocol.WebviewPanelHandle): ExtHostWebviewEditor | undefined { return this._webviewPanels.get(handle); } @@ -894,14 +768,6 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { return provider; } - private getWebviewView(handle: string): ExtHostWebviewView { - const entry = this._webviewViews.get(handle); - if (!entry) { - throw new Error('Custom document is not editable'); - } - return entry; - } - private supportEditing( provider: vscode.CustomTextEditorProvider | vscode.CustomEditorProvider | vscode.CustomReadonlyEditorProvider ): provider is vscode.CustomEditorProvider { diff --git a/src/vs/workbench/api/common/extHostWebviewView.ts b/src/vs/workbench/api/common/extHostWebviewView.ts new file mode 100644 index 00000000000..e5bb7094372 --- /dev/null +++ b/src/vs/workbench/api/common/extHostWebviewView.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 { CancellationToken } from 'vs/base/common/cancellation'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtHostWebview, ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview'; +import type * as vscode from 'vscode'; +import * as extHostProtocol from './extHost.protocol'; +import * as extHostTypes from './extHostTypes'; + +class ExtHostWebviewView extends Disposable implements vscode.WebviewView { + + readonly #handle: extHostProtocol.WebviewPanelHandle; + readonly #proxy: extHostProtocol.MainThreadWebviewsShape; + + readonly #viewType: string; + readonly #webview: ExtHostWebview; + + #isDisposed = false; + #isVisible: boolean; + #title: string | undefined; + + constructor( + handle: extHostProtocol.WebviewPanelHandle, + proxy: extHostProtocol.MainThreadWebviewsShape, + viewType: string, + webview: ExtHostWebview, + isVisible: boolean, + ) { + super(); + + this.#viewType = viewType; + this.#handle = handle; + this.#proxy = proxy; + this.#webview = webview; + this.#isVisible = isVisible; + } + + public dispose() { + if (this.#isDisposed) { + return; + } + + this.#isDisposed = true; + this.#onDidDispose.fire(); + + super.dispose(); + } + + readonly #onDidChangeVisibility = this._register(new Emitter()); + public readonly onDidChangeVisibility = this.#onDidChangeVisibility.event; + + readonly #onDidDispose = this._register(new Emitter()); + public readonly onDidDispose = this.#onDidDispose.event; + + public get title(): string | undefined { + this.assertNotDisposed(); + return this.#title; + } + + public set title(value: string | undefined) { + this.assertNotDisposed(); + if (this.#title !== value) { + this.#title = value; + this.#proxy.$setWebviewViewTitle(this.#handle, value); + } + } + + public get visible(): boolean { return this.#isVisible; } + + public get webview(): vscode.Webview { return this.#webview; } + + public get viewType(): string { return this.#viewType; } + + /* internal */ _setVisible(visible: boolean) { + if (visible === this.#isVisible) { + return; + } + + this.#isVisible = visible; + this.#onDidChangeVisibility.fire(); + } + + private assertNotDisposed() { + if (this.#isDisposed) { + throw new Error('Webview is disposed'); + } + } +} + +export class ExtHostWebviewViews implements extHostProtocol.ExtHostWebviewViewsShape { + + private readonly _proxy: extHostProtocol.MainThreadWebviewsShape; + + private readonly _viewProviders = new Map(); + + private readonly _webviewViews = new Map(); + + constructor( + mainContext: extHostProtocol.IMainContext, + private readonly _extHostWebview: ExtHostWebviews, + ) { + this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadWebviews); + } + + public registerWebviewViewProvider( + extension: IExtensionDescription, + viewType: string, + provider: vscode.WebviewViewProvider, + webviewOptions?: { + retainContextWhenHidden?: boolean + }, + ): vscode.Disposable { + if (this._viewProviders.has(viewType)) { + throw new Error(`View provider for '${viewType}' already registered`); + } + + this._viewProviders.set(viewType, { provider, extension }); + this._proxy.$registerWebviewViewProvider(viewType, webviewOptions); + + return new extHostTypes.Disposable(() => { + this._viewProviders.delete(viewType); + this._proxy.$unregisterWebviewViewProvider(viewType); + }); + } + + async $resolveWebviewView( + webviewHandle: string, + viewType: string, + state: any, + cancellation: CancellationToken, + ): Promise { + const entry = this._viewProviders.get(viewType); + if (!entry) { + throw new Error(`No view provider found for '${viewType}'`); + } + + const { provider, extension } = entry; + + const webview = this._extHostWebview.createNewWebview(webviewHandle, { /* todo */ }, extension); + const revivedView = new ExtHostWebviewView(webviewHandle, this._proxy, viewType, webview, true); + + this._webviewViews.set(webviewHandle, revivedView); + + await provider.resolveWebviewView(revivedView, { state }, cancellation); + } + + async $onDidChangeWebviewViewVisibility( + webviewHandle: string, + visible: boolean + ) { + const webviewView = this.getWebviewView(webviewHandle); + webviewView._setVisible(visible); + } + + async $disposeWebviewView(webviewHandle: string) { + const webviewView = this.getWebviewView(webviewHandle); + this._webviewViews.delete(webviewHandle); + webviewView.dispose(); + } + + private getWebviewView(handle: string): ExtHostWebviewView { + const entry = this._webviewViews.get(handle); + if (!entry) { + throw new Error('Custom document is not editable'); + } + return entry; + } +} From 6db81f6ab28257ba768ec9e0cb7f9e962418893a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 20 Aug 2020 15:48:14 -0700 Subject: [PATCH 390/736] Move custom editors into own ext host services --- .../api/browser/mainThreadWebview.ts | 23 +- .../workbench/api/common/extHost.api.impl.ts | 6 +- .../workbench/api/common/extHost.protocol.ts | 3 + .../api/common/extHostCustomEditors.ts | 386 ++++++++++++++++++ src/vs/workbench/api/common/extHostWebview.ts | 382 +---------------- .../api/common/extHostWebviewView.ts | 2 +- .../test/browser/api/extHostWebview.test.ts | 24 +- 7 files changed, 432 insertions(+), 394 deletions(-) create mode 100644 src/vs/workbench/api/common/extHostCustomEditors.ts diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index e1a231fc36f..8342aa54aa2 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -118,7 +118,9 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma ]); private readonly _proxy: extHostProtocol.ExtHostWebviewsShape; - private readonly _viewsProxy: extHostProtocol.ExtHostWebviewViewsShape; + private readonly _proxyViews: extHostProtocol.ExtHostWebviewViewsShape; + private readonly _proxyCustomEditors: extHostProtocol.ExtHostCustomEditorsShape; + private readonly _webviewInputs = new WebviewInputStore(); private readonly _revivers = new Map(); @@ -147,7 +149,8 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma super(); this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviews); - this._viewsProxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewViews); + this._proxyViews = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewViews); + this._proxyCustomEditors = context.getProxy(extHostProtocol.ExtHostContext.ExtHostCustomEditors); this._register(_editorService.onDidActiveEditorChange(() => { const activeInput = this._editorService.activeEditor; @@ -358,15 +361,15 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma } webviewView.onDidChangeVisibility(visible => { - this._viewsProxy.$onDidChangeWebviewViewVisibility(handle, visible); + this._proxyViews.$onDidChangeWebviewViewVisibility(handle, visible); }); webviewView.onDispose(() => { - this._viewsProxy.$disposeWebviewView(handle); + this._proxyViews.$disposeWebviewView(handle); }); try { - await this._viewsProxy.$resolveWebviewView(handle, viewType, state, cancellation); + await this._proxyViews.$resolveWebviewView(handle, viewType, state, cancellation); } catch (error) { onUnexpectedError(error); webviewView.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(viewType); @@ -459,13 +462,13 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma webviewInput.onMove(async (newResource: URI) => { const oldModel = modelRef; modelRef = await this.getOrCreateCustomEditorModel(modelType, newResource, viewType, {}, CancellationToken.None); - this._proxy.$onMoveCustomEditor(handle, newResource, viewType); + this._proxyCustomEditors.$onMoveCustomEditor(handle, newResource, viewType); oldModel.dispose(); }); } try { - await this._proxy.$resolveWebviewEditor(resource, handle, viewType, webviewInput.getTitle(), editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options, cancellation); + await this._proxyCustomEditors.$resolveWebviewEditor(resource, handle, viewType, webviewInput.getTitle(), editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options, cancellation); } catch (error) { onUnexpectedError(error); webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(viewType); @@ -510,7 +513,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma } case ModelType.Custom: { - const model = MainThreadCustomEditorModel.create(this._instantiationService, this._proxy, viewType, resource, options, () => { + const model = MainThreadCustomEditorModel.create(this._instantiationService, this._proxyCustomEditors, viewType, resource, options, () => { return Array.from(this._webviewInputs) .filter(editor => editor instanceof CustomEditorInput && isEqual(editor.resource, resource)) as CustomEditorInput[]; }, cancellation, this._backupService); @@ -721,7 +724,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod public static async create( instantiationService: IInstantiationService, - proxy: extHostProtocol.ExtHostWebviewsShape, + proxy: extHostProtocol.ExtHostCustomEditorsShape, viewType: string, resource: URI, options: { backupId?: string }, @@ -734,7 +737,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod } constructor( - private readonly _proxy: extHostProtocol.ExtHostWebviewsShape, + private readonly _proxy: extHostProtocol.ExtHostCustomEditorsShape, private readonly _viewType: string, private readonly _editorResource: URI, fromBackup: boolean, diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index d36555cf30e..3778f388571 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -77,6 +77,7 @@ import { ExtHostNotebookConcatDocument } from 'vs/workbench/api/common/extHostNo import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer'; import { ExtHostWebviewViews } from 'vs/workbench/api/common/extHostWebviewView'; +import { ExtHostCustomEditors } from 'vs/workbench/api/common/extHostCustomEditors'; export interface IExtensionApiFactory { (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; @@ -142,7 +143,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostTheming = rpcProtocol.set(ExtHostContext.ExtHostTheming, new ExtHostTheming(rpcProtocol)); const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol)); const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol, extHostCommands)); - const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace, extHostLogService, extHostApiDeprecation, extHostDocuments, extensionStoragePaths)); + const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace, extHostLogService, extHostApiDeprecation)); + const extHostCustomEditors = rpcProtocol.set(ExtHostContext.ExtHostCustomEditors, new ExtHostCustomEditors(rpcProtocol, extHostDocuments, extensionStoragePaths, extHostWebviews)); const extHostWebviewViews = rpcProtocol.set(ExtHostContext.ExtHostWebviewViews, new ExtHostWebviewViews(rpcProtocol, extHostWebviews)); // Check that no named customers are missing @@ -594,7 +596,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializer); }, registerCustomEditorProvider: (viewType: string, provider: vscode.CustomTextEditorProvider | vscode.CustomReadonlyEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerDocument?: boolean } = {}) => { - return extHostWebviews.registerCustomEditorProvider(extension, viewType, provider, options); + return extHostCustomEditors.registerCustomEditorProvider(extension, viewType, provider, options); }, registerDecorationProvider(provider: vscode.DecorationProvider) { checkProposedApiEnabled(extension); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 2546199fcc3..acbd96b304a 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -651,7 +651,9 @@ export interface ExtHostWebviewsShape { $onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise; $deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; +} +export interface ExtHostCustomEditorsShape { $resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, cancellation: CancellationToken): Promise; $createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken): Promise<{ editable: boolean }>; $disposeCustomDocument(resource: UriComponents, viewType: string): Promise; @@ -1749,6 +1751,7 @@ export const ExtHostContext = { ExtHostWorkspace: createExtId('ExtHostWorkspace'), ExtHostWindow: createExtId('ExtHostWindow'), ExtHostWebviews: createExtId('ExtHostWebviews'), + ExtHostCustomEditors: createExtId('ExtHostCustomEditors'), ExtHostWebviewViews: createExtId('ExtHostWebviewViews'), ExtHostEditorInsets: createExtId('ExtHostEditorInsets'), ExtHostProgress: createMainId('ExtHostProgress'), diff --git a/src/vs/workbench/api/common/extHostCustomEditors.ts b/src/vs/workbench/api/common/extHostCustomEditors.ts new file mode 100644 index 00000000000..f8275fab414 --- /dev/null +++ b/src/vs/workbench/api/common/extHostCustomEditors.ts @@ -0,0 +1,386 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken } from 'vs/base/common/cancellation'; +import { hash } from 'vs/base/common/hash'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; +import { joinPath } from 'vs/base/common/resources'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import * as modes from 'vs/editor/common/modes'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; +import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; +import { ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview'; +import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; +import type * as vscode from 'vscode'; +import { Cache } from './cache'; +import * as extHostProtocol from './extHost.protocol'; +import * as extHostTypes from './extHostTypes'; + + +class CustomDocumentStoreEntry { + + private _backupCounter = 1; + + constructor( + public readonly document: vscode.CustomDocument, + private readonly _storagePath: URI | undefined, + ) { } + + private readonly _edits = new Cache('custom documents'); + + private _backup?: vscode.CustomDocumentBackup; + + addEdit(item: vscode.CustomDocumentEditEvent): number { + return this._edits.add([item]); + } + + async undo(editId: number, isDirty: boolean): Promise { + await this.getEdit(editId).undo(); + if (!isDirty) { + this.disposeBackup(); + } + } + + async redo(editId: number, isDirty: boolean): Promise { + await this.getEdit(editId).redo(); + if (!isDirty) { + this.disposeBackup(); + } + } + + disposeEdits(editIds: number[]): void { + for (const id of editIds) { + this._edits.delete(id); + } + } + + getNewBackupUri(): URI { + if (!this._storagePath) { + throw new Error('Backup requires a valid storage path'); + } + const fileName = hashPath(this.document.uri) + (this._backupCounter++); + return joinPath(this._storagePath, fileName); + } + + updateBackup(backup: vscode.CustomDocumentBackup): void { + this._backup?.delete(); + this._backup = backup; + } + + disposeBackup(): void { + this._backup?.delete(); + this._backup = undefined; + } + + private getEdit(editId: number): vscode.CustomDocumentEditEvent { + const edit = this._edits.get(editId, 0); + if (!edit) { + throw new Error('No edit found'); + } + return edit; + } +} + +class CustomDocumentStore { + private readonly _documents = new Map(); + + public get(viewType: string, resource: vscode.Uri): CustomDocumentStoreEntry | undefined { + return this._documents.get(this.key(viewType, resource)); + } + + public add(viewType: string, document: vscode.CustomDocument, storagePath: URI | undefined): CustomDocumentStoreEntry { + const key = this.key(viewType, document.uri); + if (this._documents.has(key)) { + throw new Error(`Document already exists for viewType:${viewType} resource:${document.uri}`); + } + const entry = new CustomDocumentStoreEntry(document, storagePath); + this._documents.set(key, entry); + return entry; + } + + public delete(viewType: string, document: vscode.CustomDocument) { + const key = this.key(viewType, document.uri); + this._documents.delete(key); + } + + private key(viewType: string, resource: vscode.Uri): string { + return `${viewType}@@@${resource}`; + } + +} + +const enum WebviewEditorType { + Text, + Custom +} + +type ProviderEntry = { + readonly extension: IExtensionDescription; + readonly type: WebviewEditorType.Text; + readonly provider: vscode.CustomTextEditorProvider; +} | { + readonly extension: IExtensionDescription; + readonly type: WebviewEditorType.Custom; + readonly provider: vscode.CustomReadonlyEditorProvider; +}; + +class EditorProviderStore { + private readonly _providers = new Map(); + + public addTextProvider(viewType: string, extension: IExtensionDescription, provider: vscode.CustomTextEditorProvider): vscode.Disposable { + return this.add(WebviewEditorType.Text, viewType, extension, provider); + } + + public addCustomProvider(viewType: string, extension: IExtensionDescription, provider: vscode.CustomReadonlyEditorProvider): vscode.Disposable { + return this.add(WebviewEditorType.Custom, viewType, extension, provider); + } + + public get(viewType: string): ProviderEntry | undefined { + return this._providers.get(viewType); + } + + private add(type: WebviewEditorType, viewType: string, extension: IExtensionDescription, provider: vscode.CustomTextEditorProvider | vscode.CustomReadonlyEditorProvider): vscode.Disposable { + if (this._providers.has(viewType)) { + throw new Error(`Provider for viewType:${viewType} already registered`); + } + this._providers.set(viewType, { type, extension, provider } as ProviderEntry); + return new extHostTypes.Disposable(() => this._providers.delete(viewType)); + } +} + +export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditorsShape { + + private readonly _proxy: extHostProtocol.MainThreadWebviewsShape; + + private readonly _editorProviders = new EditorProviderStore(); + + private readonly _documents = new CustomDocumentStore(); + + constructor( + mainContext: extHostProtocol.IMainContext, + private readonly _extHostDocuments: ExtHostDocuments, + private readonly _extensionStoragePaths: IExtensionStoragePaths | undefined, + private readonly _extHostWebview: ExtHostWebviews, + ) { + this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadWebviews); + } + + public registerCustomEditorProvider( + extension: IExtensionDescription, + viewType: string, + provider: vscode.CustomReadonlyEditorProvider | vscode.CustomTextEditorProvider, + options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerDocument?: boolean }, + ): vscode.Disposable { + const disposables = new DisposableStore(); + if ('resolveCustomTextEditor' in provider) { + disposables.add(this._editorProviders.addTextProvider(viewType, extension, provider)); + this._proxy.$registerTextEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, { + supportsMove: !!provider.moveCustomTextEditor, + }); + } else { + disposables.add(this._editorProviders.addCustomProvider(viewType, extension, provider)); + + if (this.supportEditing(provider)) { + disposables.add(provider.onDidChangeCustomDocument(e => { + const entry = this.getCustomDocumentEntry(viewType, e.document.uri); + if (isEditEvent(e)) { + const editId = entry.addEdit(e); + this._proxy.$onDidEdit(e.document.uri, viewType, editId, e.label); + } else { + this._proxy.$onContentChange(e.document.uri, viewType); + } + })); + } + + this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, !!options.supportsMultipleEditorsPerDocument); + } + + return extHostTypes.Disposable.from( + disposables, + new extHostTypes.Disposable(() => { + this._proxy.$unregisterEditorProvider(viewType); + })); + } + + + async $createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken) { + const entry = this._editorProviders.get(viewType); + if (!entry) { + throw new Error(`No provider found for '${viewType}'`); + } + + if (entry.type !== WebviewEditorType.Custom) { + throw new Error(`Invalid provide type for '${viewType}'`); + } + + const revivedResource = URI.revive(resource); + const document = await entry.provider.openCustomDocument(revivedResource, { backupId }, cancellation); + + let storageRoot: URI | undefined; + if (this.supportEditing(entry.provider) && this._extensionStoragePaths) { + storageRoot = this._extensionStoragePaths.workspaceValue(entry.extension) ?? this._extensionStoragePaths.globalValue(entry.extension); + } + this._documents.add(viewType, document, storageRoot); + + return { editable: this.supportEditing(entry.provider) }; + } + + async $disposeCustomDocument(resource: UriComponents, viewType: string): Promise { + const entry = this._editorProviders.get(viewType); + if (!entry) { + throw new Error(`No provider found for '${viewType}'`); + } + + if (entry.type !== WebviewEditorType.Custom) { + throw new Error(`Invalid provider type for '${viewType}'`); + } + + const revivedResource = URI.revive(resource); + const { document } = this.getCustomDocumentEntry(viewType, revivedResource); + this._documents.delete(viewType, document); + document.dispose(); + } + + async $resolveWebviewEditor( + resource: UriComponents, + handle: extHostProtocol.WebviewPanelHandle, + viewType: string, + title: string, + position: EditorViewColumn, + options: modes.IWebviewOptions & modes.IWebviewPanelOptions, + cancellation: CancellationToken, + ): Promise { + const entry = this._editorProviders.get(viewType); + if (!entry) { + throw new Error(`No provider found for '${viewType}'`); + } + + const webview = this._extHostWebview.createNewWebview(handle, options, entry.extension); + const panel = this._extHostWebview.createNewWebviewPanel(handle, viewType, title, position, options, webview); + + const revivedResource = URI.revive(resource); + + switch (entry.type) { + case WebviewEditorType.Custom: + { + const { document } = this.getCustomDocumentEntry(viewType, revivedResource); + return entry.provider.resolveCustomEditor(document, panel, cancellation); + } + case WebviewEditorType.Text: + { + const document = this._extHostDocuments.getDocument(revivedResource); + return entry.provider.resolveCustomTextEditor(document, panel, cancellation); + } + default: + { + throw new Error('Unknown webview provider type'); + } + } + } + + $disposeEdits(resourceComponents: UriComponents, viewType: string, editIds: number[]): void { + const document = this.getCustomDocumentEntry(viewType, resourceComponents); + document.disposeEdits(editIds); + } + + async $onMoveCustomEditor(handle: string, newResourceComponents: UriComponents, viewType: string): Promise { + const entry = this._editorProviders.get(viewType); + if (!entry) { + throw new Error(`No provider found for '${viewType}'`); + } + + if (!(entry.provider as vscode.CustomTextEditorProvider).moveCustomTextEditor) { + throw new Error(`Provider does not implement move '${viewType}'`); + } + + const webview = this._extHostWebview.getWebviewPanel(handle); + if (!webview) { + throw new Error(`No webview found`); + } + + const resource = URI.revive(newResourceComponents); + const document = this._extHostDocuments.getDocument(resource); + await (entry.provider as vscode.CustomTextEditorProvider).moveCustomTextEditor!(document, webview, CancellationToken.None); + } + + async $undo(resourceComponents: UriComponents, viewType: string, editId: number, isDirty: boolean): Promise { + const entry = this.getCustomDocumentEntry(viewType, resourceComponents); + return entry.undo(editId, isDirty); + } + + async $redo(resourceComponents: UriComponents, viewType: string, editId: number, isDirty: boolean): Promise { + const entry = this.getCustomDocumentEntry(viewType, resourceComponents); + return entry.redo(editId, isDirty); + } + + async $revert(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise { + const entry = this.getCustomDocumentEntry(viewType, resourceComponents); + const provider = this.getCustomEditorProvider(viewType); + await provider.revertCustomDocument(entry.document, cancellation); + entry.disposeBackup(); + } + + async $onSave(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise { + const entry = this.getCustomDocumentEntry(viewType, resourceComponents); + const provider = this.getCustomEditorProvider(viewType); + await provider.saveCustomDocument(entry.document, cancellation); + entry.disposeBackup(); + } + + async $onSaveAs(resourceComponents: UriComponents, viewType: string, targetResource: UriComponents, cancellation: CancellationToken): Promise { + const entry = this.getCustomDocumentEntry(viewType, resourceComponents); + const provider = this.getCustomEditorProvider(viewType); + return provider.saveCustomDocumentAs(entry.document, URI.revive(targetResource), cancellation); + } + + async $backup(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise { + const entry = this.getCustomDocumentEntry(viewType, resourceComponents); + const provider = this.getCustomEditorProvider(viewType); + + const backup = await provider.backupCustomDocument(entry.document, { + destination: entry.getNewBackupUri(), + }, cancellation); + entry.updateBackup(backup); + return backup.id; + } + + + private getCustomDocumentEntry(viewType: string, resource: UriComponents): CustomDocumentStoreEntry { + const entry = this._documents.get(viewType, URI.revive(resource)); + if (!entry) { + throw new Error('No custom document found'); + } + return entry; + } + + private getCustomEditorProvider(viewType: string): vscode.CustomEditorProvider { + const entry = this._editorProviders.get(viewType); + const provider = entry?.provider; + if (!provider || !this.supportEditing(provider)) { + throw new Error('Custom document is not editable'); + } + return provider; + } + + private supportEditing( + provider: vscode.CustomTextEditorProvider | vscode.CustomEditorProvider | vscode.CustomReadonlyEditorProvider + ): provider is vscode.CustomEditorProvider { + return !!(provider as vscode.CustomEditorProvider).onDidChangeCustomDocument; + } +} + + +function isEditEvent(e: vscode.CustomDocumentContentChangeEvent | vscode.CustomDocumentEditEvent): e is vscode.CustomDocumentEditEvent { + return typeof (e as vscode.CustomDocumentEditEvent).undo === 'function' + && typeof (e as vscode.CustomDocumentEditEvent).redo === 'function'; +} + +function hashPath(resource: URI): string { + const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString(); + return hash(str) + ''; +} + diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index 2e74c83c3cf..be7200fdb96 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -3,31 +3,22 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; -import { hash } from 'vs/base/common/hash'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { Schemas } from 'vs/base/common/network'; -import { joinPath } from 'vs/base/common/resources'; -import { URI, UriComponents } from 'vs/base/common/uri'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import * as modes from 'vs/editor/common/modes'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; -import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; -import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; import type * as vscode from 'vscode'; -import { Cache } from './cache'; import * as extHostProtocol from './extHost.protocol'; import * as extHostTypes from './extHostTypes'; -type IconPath = URI | { light: URI, dark: URI }; - export class ExtHostWebview implements vscode.Webview { readonly #handle: extHostProtocol.WebviewPanelHandle; @@ -125,7 +116,10 @@ export class ExtHostWebview implements vscode.Webview { } } -export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPanel { +type IconPath = URI | { light: URI, dark: URI }; + + +class ExtHostWebviewPanel extends Disposable implements vscode.WebviewPanel { readonly #handle: extHostProtocol.WebviewPanelHandle; readonly #proxy: extHostProtocol.MainThreadWebviewsShape; @@ -274,137 +268,6 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa } } -class CustomDocumentStoreEntry { - - private _backupCounter = 1; - - constructor( - public readonly document: vscode.CustomDocument, - private readonly _storagePath: URI | undefined, - ) { } - - private readonly _edits = new Cache('custom documents'); - - private _backup?: vscode.CustomDocumentBackup; - - addEdit(item: vscode.CustomDocumentEditEvent): number { - return this._edits.add([item]); - } - - async undo(editId: number, isDirty: boolean): Promise { - await this.getEdit(editId).undo(); - if (!isDirty) { - this.disposeBackup(); - } - } - - async redo(editId: number, isDirty: boolean): Promise { - await this.getEdit(editId).redo(); - if (!isDirty) { - this.disposeBackup(); - } - } - - disposeEdits(editIds: number[]): void { - for (const id of editIds) { - this._edits.delete(id); - } - } - - getNewBackupUri(): URI { - if (!this._storagePath) { - throw new Error('Backup requires a valid storage path'); - } - const fileName = hashPath(this.document.uri) + (this._backupCounter++); - return joinPath(this._storagePath, fileName); - } - - updateBackup(backup: vscode.CustomDocumentBackup): void { - this._backup?.delete(); - this._backup = backup; - } - - disposeBackup(): void { - this._backup?.delete(); - this._backup = undefined; - } - - private getEdit(editId: number): vscode.CustomDocumentEditEvent { - const edit = this._edits.get(editId, 0); - if (!edit) { - throw new Error('No edit found'); - } - return edit; - } -} - -class CustomDocumentStore { - private readonly _documents = new Map(); - - public get(viewType: string, resource: vscode.Uri): CustomDocumentStoreEntry | undefined { - return this._documents.get(this.key(viewType, resource)); - } - - public add(viewType: string, document: vscode.CustomDocument, storagePath: URI | undefined): CustomDocumentStoreEntry { - const key = this.key(viewType, document.uri); - if (this._documents.has(key)) { - throw new Error(`Document already exists for viewType:${viewType} resource:${document.uri}`); - } - const entry = new CustomDocumentStoreEntry(document, storagePath); - this._documents.set(key, entry); - return entry; - } - - public delete(viewType: string, document: vscode.CustomDocument) { - const key = this.key(viewType, document.uri); - this._documents.delete(key); - } - - private key(viewType: string, resource: vscode.Uri): string { - return `${viewType}@@@${resource}`; - } - -} - -const enum WebviewEditorType { - Text, - Custom -} - -type ProviderEntry = { - readonly extension: IExtensionDescription; - readonly type: WebviewEditorType.Text; - readonly provider: vscode.CustomTextEditorProvider; -} | { - readonly extension: IExtensionDescription; - readonly type: WebviewEditorType.Custom; - readonly provider: vscode.CustomReadonlyEditorProvider; -}; - -class EditorProviderStore { - private readonly _providers = new Map(); - - public addTextProvider(viewType: string, extension: IExtensionDescription, provider: vscode.CustomTextEditorProvider): vscode.Disposable { - return this.add(WebviewEditorType.Text, viewType, extension, provider); - } - - public addCustomProvider(viewType: string, extension: IExtensionDescription, provider: vscode.CustomReadonlyEditorProvider): vscode.Disposable { - return this.add(WebviewEditorType.Custom, viewType, extension, provider); - } - - public get(viewType: string): ProviderEntry | undefined { - return this._providers.get(viewType); - } - - private add(type: WebviewEditorType, viewType: string, extension: IExtensionDescription, provider: vscode.CustomTextEditorProvider | vscode.CustomReadonlyEditorProvider): vscode.Disposable { - if (this._providers.has(viewType)) { - throw new Error(`Provider for viewType:${viewType} already registered`); - } - this._providers.set(viewType, { type, extension, provider } as ProviderEntry); - return new extHostTypes.Disposable(() => this._providers.delete(viewType)); - } -} - export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { private static newHandle(): extHostProtocol.WebviewPanelHandle { @@ -414,25 +277,19 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { private readonly _proxy: extHostProtocol.MainThreadWebviewsShape; private readonly _webviews = new Map(); - private readonly _webviewPanels = new Map(); + private readonly _webviewPanels = new Map(); private readonly _serializers = new Map(); - private readonly _editorProviders = new EditorProviderStore(); - - private readonly _documents = new CustomDocumentStore(); - constructor( mainContext: extHostProtocol.IMainContext, private readonly initData: WebviewInitData, private readonly workspace: IExtHostWorkspace | undefined, private readonly _logService: ILogService, private readonly _deprecationService: IExtHostApiDeprecationService, - private readonly _extHostDocuments: ExtHostDocuments, - private readonly _extensionStoragePaths?: IExtensionStoragePaths, ) { this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadWebviews); } @@ -454,8 +311,8 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { this._proxy.$createWebviewPanel(toExtensionData(extension), handle, viewType, title, webviewShowOptions, convertWebviewOptions(extension, this.workspace, options)); const webview = this.createNewWebview(handle, options, extension); - const panel = new ExtHostWebviewEditor(handle, this._proxy, viewType, title, viewColumn, options, webview); - this._webviewPanels.set(handle, panel); + const panel = this.createNewWebviewPanel(handle, viewType, title, viewColumn, options, webview); + return panel; } @@ -477,43 +334,6 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { }); } - public registerCustomEditorProvider( - extension: IExtensionDescription, - viewType: string, - provider: vscode.CustomReadonlyEditorProvider | vscode.CustomTextEditorProvider, - options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerDocument?: boolean }, - ): vscode.Disposable { - const disposables = new DisposableStore(); - if ('resolveCustomTextEditor' in provider) { - disposables.add(this._editorProviders.addTextProvider(viewType, extension, provider)); - this._proxy.$registerTextEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, { - supportsMove: !!provider.moveCustomTextEditor, - }); - } else { - disposables.add(this._editorProviders.addCustomProvider(viewType, extension, provider)); - - if (this.supportEditing(provider)) { - disposables.add(provider.onDidChangeCustomDocument(e => { - const entry = this.getCustomDocumentEntry(viewType, e.document.uri); - if (isEditEvent(e)) { - const editId = entry.addEdit(e); - this._proxy.$onDidEdit(e.document.uri, viewType, editId, e.label); - } else { - this._proxy.$onContentChange(e.document.uri, viewType); - } - })); - } - - this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, !!options.supportsMultipleEditorsPerDocument); - } - - return extHostTypes.Disposable.from( - disposables, - new extHostTypes.Disposable(() => { - this._proxy.$unregisterEditorProvider(viewType); - })); - } - public $onMessage( handle: extHostProtocol.WebviewPanelHandle, message: any @@ -587,151 +407,14 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { const { serializer, extension } = entry; const webview = this.createNewWebview(webviewHandle, options, extension); - const revivedPanel = new ExtHostWebviewEditor(webviewHandle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview); - this._webviewPanels.set(webviewHandle, revivedPanel); + const revivedPanel = this.createNewWebviewPanel(webviewHandle, viewType, title, position, options, webview); await serializer.deserializeWebviewPanel(revivedPanel, state); } - async $createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken) { - const entry = this._editorProviders.get(viewType); - if (!entry) { - throw new Error(`No provider found for '${viewType}'`); - } - - if (entry.type !== WebviewEditorType.Custom) { - throw new Error(`Invalid provide type for '${viewType}'`); - } - - const revivedResource = URI.revive(resource); - const document = await entry.provider.openCustomDocument(revivedResource, { backupId }, cancellation); - - let storageRoot: URI | undefined; - if (this.supportEditing(entry.provider) && this._extensionStoragePaths) { - storageRoot = this._extensionStoragePaths.workspaceValue(entry.extension) ?? this._extensionStoragePaths.globalValue(entry.extension); - } - this._documents.add(viewType, document, storageRoot); - - return { editable: this.supportEditing(entry.provider) }; - } - - async $disposeCustomDocument(resource: UriComponents, viewType: string): Promise { - const entry = this._editorProviders.get(viewType); - if (!entry) { - throw new Error(`No provider found for '${viewType}'`); - } - - if (entry.type !== WebviewEditorType.Custom) { - throw new Error(`Invalid provider type for '${viewType}'`); - } - - const revivedResource = URI.revive(resource); - const { document } = this.getCustomDocumentEntry(viewType, revivedResource); - this._documents.delete(viewType, document); - document.dispose(); - } - - async $resolveWebviewEditor( - resource: UriComponents, - handle: extHostProtocol.WebviewPanelHandle, - viewType: string, - title: string, - position: EditorViewColumn, - options: modes.IWebviewOptions & modes.IWebviewPanelOptions, - cancellation: CancellationToken, - ): Promise { - const entry = this._editorProviders.get(viewType); - if (!entry) { - throw new Error(`No provider found for '${viewType}'`); - } - - const webview = this.createNewWebview(handle, options, entry.extension); - const revivedPanel = new ExtHostWebviewEditor(handle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview); - this._webviewPanels.set(handle, revivedPanel); - - const revivedResource = URI.revive(resource); - - switch (entry.type) { - case WebviewEditorType.Custom: - { - const { document } = this.getCustomDocumentEntry(viewType, revivedResource); - return entry.provider.resolveCustomEditor(document, revivedPanel, cancellation); - } - case WebviewEditorType.Text: - { - const document = this._extHostDocuments.getDocument(revivedResource); - return entry.provider.resolveCustomTextEditor(document, revivedPanel, cancellation); - } - default: - { - throw new Error('Unknown webview provider type'); - } - } - } - - $disposeEdits(resourceComponents: UriComponents, viewType: string, editIds: number[]): void { - const document = this.getCustomDocumentEntry(viewType, resourceComponents); - document.disposeEdits(editIds); - } - - async $onMoveCustomEditor(handle: string, newResourceComponents: UriComponents, viewType: string): Promise { - const entry = this._editorProviders.get(viewType); - if (!entry) { - throw new Error(`No provider found for '${viewType}'`); - } - - if (!(entry.provider as vscode.CustomTextEditorProvider).moveCustomTextEditor) { - throw new Error(`Provider does not implement move '${viewType}'`); - } - - const webview = this.getWebviewPanel(handle); - if (!webview) { - throw new Error(`No webview found`); - } - - const resource = URI.revive(newResourceComponents); - const document = this._extHostDocuments.getDocument(resource); - await (entry.provider as vscode.CustomTextEditorProvider).moveCustomTextEditor!(document, webview, CancellationToken.None); - } - - async $undo(resourceComponents: UriComponents, viewType: string, editId: number, isDirty: boolean): Promise { - const entry = this.getCustomDocumentEntry(viewType, resourceComponents); - return entry.undo(editId, isDirty); - } - - async $redo(resourceComponents: UriComponents, viewType: string, editId: number, isDirty: boolean): Promise { - const entry = this.getCustomDocumentEntry(viewType, resourceComponents); - return entry.redo(editId, isDirty); - } - - async $revert(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise { - const entry = this.getCustomDocumentEntry(viewType, resourceComponents); - const provider = this.getCustomEditorProvider(viewType); - await provider.revertCustomDocument(entry.document, cancellation); - entry.disposeBackup(); - } - - async $onSave(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise { - const entry = this.getCustomDocumentEntry(viewType, resourceComponents); - const provider = this.getCustomEditorProvider(viewType); - await provider.saveCustomDocument(entry.document, cancellation); - entry.disposeBackup(); - } - - async $onSaveAs(resourceComponents: UriComponents, viewType: string, targetResource: UriComponents, cancellation: CancellationToken): Promise { - const entry = this.getCustomDocumentEntry(viewType, resourceComponents); - const provider = this.getCustomEditorProvider(viewType); - return provider.saveCustomDocumentAs(entry.document, URI.revive(targetResource), cancellation); - } - - async $backup(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise { - const entry = this.getCustomDocumentEntry(viewType, resourceComponents); - const provider = this.getCustomEditorProvider(viewType); - - const backup = await provider.backupCustomDocument(entry.document, { - destination: entry.getNewBackupUri(), - }, cancellation); - entry.updateBackup(backup); - return backup.id; + public createNewWebviewPanel(webviewHandle: string, viewType: string, title: string, position: number, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, webview: ExtHostWebview) { + const panel = new ExtHostWebviewPanel(webviewHandle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview); + this._webviewPanels.set(webviewHandle, panel); + return panel; } public createNewWebview(handle: string, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, extension: IExtensionDescription): ExtHostWebview { @@ -747,35 +430,12 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { return this._webviews.get(handle); } - private getWebviewPanel(handle: extHostProtocol.WebviewPanelHandle): ExtHostWebviewEditor | undefined { + public getWebviewPanel(handle: extHostProtocol.WebviewPanelHandle): ExtHostWebviewPanel | undefined { return this._webviewPanels.get(handle); } - - private getCustomDocumentEntry(viewType: string, resource: UriComponents): CustomDocumentStoreEntry { - const entry = this._documents.get(viewType, URI.revive(resource)); - if (!entry) { - throw new Error('No custom document found'); - } - return entry; - } - - private getCustomEditorProvider(viewType: string): vscode.CustomEditorProvider { - const entry = this._editorProviders.get(viewType); - const provider = entry?.provider; - if (!provider || !this.supportEditing(provider)) { - throw new Error('Custom document is not editable'); - } - return provider; - } - - private supportEditing( - provider: vscode.CustomTextEditorProvider | vscode.CustomEditorProvider | vscode.CustomReadonlyEditorProvider - ): provider is vscode.CustomEditorProvider { - return !!(provider as vscode.CustomEditorProvider).onDidChangeCustomDocument; - } } -function toExtensionData(extension: IExtensionDescription): extHostProtocol.WebviewExtensionDescription { +export function toExtensionData(extension: IExtensionDescription): extHostProtocol.WebviewExtensionDescription { return { id: extension.identifier, location: extension.extensionLocation }; } @@ -808,13 +468,3 @@ function getDefaultLocalResourceRoots( extension.extensionLocation, ]; } - -function isEditEvent(e: vscode.CustomDocumentContentChangeEvent | vscode.CustomDocumentEditEvent): e is vscode.CustomDocumentEditEvent { - return typeof (e as vscode.CustomDocumentEditEvent).undo === 'function' - && typeof (e as vscode.CustomDocumentEditEvent).redo === 'function'; -} - -function hashPath(resource: URI): string { - const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString(); - return hash(str) + ''; -} diff --git a/src/vs/workbench/api/common/extHostWebviewView.ts b/src/vs/workbench/api/common/extHostWebviewView.ts index e5bb7094372..f8162cc0669 100644 --- a/src/vs/workbench/api/common/extHostWebviewView.ts +++ b/src/vs/workbench/api/common/extHostWebviewView.ts @@ -169,7 +169,7 @@ export class ExtHostWebviewViews implements extHostProtocol.ExtHostWebviewViewsS private getWebviewView(handle: string): ExtHostWebviewView { const entry = this._webviewViews.get(handle); if (!entry) { - throw new Error('Custom document is not editable'); + throw new Error('No webview found'); } return entry; } diff --git a/src/vs/workbench/test/browser/api/extHostWebview.test.ts b/src/vs/workbench/test/browser/api/extHostWebview.test.ts index f74b2998d27..4e2046729d6 100644 --- a/src/vs/workbench/test/browser/api/extHostWebview.test.ts +++ b/src/vs/workbench/test/browser/api/extHostWebview.test.ts @@ -3,33 +3,27 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import type * as vscode from 'vscode'; import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; +import { mock } from 'vs/base/test/common/mock'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { NullLogService } from 'vs/platform/log/common/log'; import { MainThreadWebviews } from 'vs/workbench/api/browser/mainThreadWebview'; -import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview'; -import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; -import { mock } from 'vs/base/test/common/mock'; -import { SingleProxyRPCProtocol } from './testRPCProtocol'; -import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; -import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; -import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { NullApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; +import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; +import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview'; +import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; +import type * as vscode from 'vscode'; +import { SingleProxyRPCProtocol } from './testRPCProtocol'; suite('ExtHostWebview', () => { let rpcProtocol: (IExtHostRpcService & IExtHostContext) | undefined; - let extHostDocuments: ExtHostDocuments | undefined; setup(() => { const shape = createNoopMainThreadWebviews(); rpcProtocol = SingleProxyRPCProtocol(shape); - - const extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(rpcProtocol, new NullLogService()); - extHostDocuments = new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors); }); test('Cannot register multiple serializers for the same view type', async () => { @@ -39,7 +33,7 @@ suite('ExtHostWebview', () => { webviewCspSource: '', webviewResourceRoot: '', isExtensionDevelopmentDebug: false, - }, undefined, new NullLogService(), NullApiDeprecationService, extHostDocuments!); + }, undefined, new NullLogService(), NullApiDeprecationService); let lastInvokedDeserializer: vscode.WebviewPanelSerializer | undefined = undefined; @@ -76,7 +70,7 @@ suite('ExtHostWebview', () => { webviewCspSource: '', webviewResourceRoot: 'vscode-resource://{{resource}}', isExtensionDevelopmentDebug: false, - }, undefined, new NullLogService(), NullApiDeprecationService, extHostDocuments!); + }, undefined, new NullLogService(), NullApiDeprecationService); const webview = extHostWebviews.createWebviewPanel({} as any, 'type', 'title', 1, {}); assert.strictEqual( @@ -115,7 +109,7 @@ suite('ExtHostWebview', () => { webviewCspSource: '', webviewResourceRoot: `https://{{uuid}}.webview.contoso.com/commit/{{resource}}`, isExtensionDevelopmentDebug: false, - }, undefined, new NullLogService(), NullApiDeprecationService, extHostDocuments!); + }, undefined, new NullLogService(), NullApiDeprecationService); const webview = extHostWebviews.createWebviewPanel({} as any, 'type', 'title', 1, {}); function stripEndpointUuid(input: string) { From daf5143e358d01343572d327f454808151b75f70 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 20 Aug 2020 15:57:09 -0700 Subject: [PATCH 391/736] Split webview serializers into own ext host service --- .../api/browser/mainThreadWebview.ts | 4 +- .../workbench/api/common/extHost.api.impl.ts | 4 +- .../workbench/api/common/extHost.protocol.ts | 3 + src/vs/workbench/api/common/extHostWebview.ts | 43 ------------ .../api/common/extHostWebviewSerializer.ts | 66 +++++++++++++++++++ .../test/browser/api/extHostWebview.test.ts | 13 ++-- 6 files changed, 83 insertions(+), 50 deletions(-) create mode 100644 src/vs/workbench/api/common/extHostWebviewSerializer.ts diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index 8342aa54aa2..8d2d1a3f72e 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -118,6 +118,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma ]); private readonly _proxy: extHostProtocol.ExtHostWebviewsShape; + private readonly _proxySerializer: extHostProtocol.ExtHostWebviewSerializerShape; private readonly _proxyViews: extHostProtocol.ExtHostWebviewViewsShape; private readonly _proxyCustomEditors: extHostProtocol.ExtHostCustomEditorsShape; @@ -149,6 +150,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma super(); this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviews); + this._proxySerializer = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewSerializer); this._proxyViews = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewViews); this._proxyCustomEditors = context.getProxy(extHostProtocol.ExtHostContext.ExtHostCustomEditors); @@ -316,7 +318,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma } try { - await this._proxy.$deserializeWebviewPanel(handle, viewType, webviewInput.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options); + await this._proxySerializer.$deserializeWebviewPanel(handle, viewType, webviewInput.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options); } catch (error) { onUnexpectedError(error); webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(viewType); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 3778f388571..c6dabd22fa1 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -78,6 +78,7 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer'; import { ExtHostWebviewViews } from 'vs/workbench/api/common/extHostWebviewView'; import { ExtHostCustomEditors } from 'vs/workbench/api/common/extHostCustomEditors'; +import { ExtHostWebviewSerializer } from 'vs/workbench/api/common/extHostWebviewSerializer'; export interface IExtensionApiFactory { (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; @@ -144,6 +145,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol)); const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol, extHostCommands)); const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace, extHostLogService, extHostApiDeprecation)); + const extHostWebviewSerializers = rpcProtocol.set(ExtHostContext.ExtHostWebviewSerializer, new ExtHostWebviewSerializer(rpcProtocol, extHostWebviews)); const extHostCustomEditors = rpcProtocol.set(ExtHostContext.ExtHostCustomEditors, new ExtHostCustomEditors(rpcProtocol, extHostDocuments, extensionStoragePaths, extHostWebviews)); const extHostWebviewViews = rpcProtocol.set(ExtHostContext.ExtHostWebviewViews, new ExtHostWebviewViews(rpcProtocol, extHostWebviews)); @@ -593,7 +595,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostTreeViews.createTreeView(viewId, options, extension); }, registerWebviewPanelSerializer: (viewType: string, serializer: vscode.WebviewPanelSerializer) => { - return extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializer); + return extHostWebviewSerializers.registerWebviewPanelSerializer(extension, viewType, serializer); }, registerCustomEditorProvider: (viewType: string, provider: vscode.CustomTextEditorProvider | vscode.CustomReadonlyEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerDocument?: boolean } = {}) => { return extHostCustomEditors.registerCustomEditorProvider(extension, viewType, provider, options); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index acbd96b304a..0fa76f27f52 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -649,7 +649,9 @@ export interface ExtHostWebviewsShape { $onMissingCsp(handle: WebviewPanelHandle, extensionId: string): void; $onDidChangeWebviewPanelViewStates(newState: WebviewPanelViewStateData): void; $onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise; +} +export interface ExtHostWebviewSerializerShape { $deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; } @@ -1751,6 +1753,7 @@ export const ExtHostContext = { ExtHostWorkspace: createExtId('ExtHostWorkspace'), ExtHostWindow: createExtId('ExtHostWindow'), ExtHostWebviews: createExtId('ExtHostWebviews'), + ExtHostWebviewSerializer: createExtId('ExtHostWebviewSerializer'), ExtHostCustomEditors: createExtId('ExtHostCustomEditors'), ExtHostWebviewViews: createExtId('ExtHostWebviewViews'), ExtHostEditorInsets: createExtId('ExtHostEditorInsets'), diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index be7200fdb96..1c9c1ac5741 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -13,11 +13,9 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; -import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; import type * as vscode from 'vscode'; import * as extHostProtocol from './extHost.protocol'; -import * as extHostTypes from './extHostTypes'; export class ExtHostWebview implements vscode.Webview { @@ -279,10 +277,6 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { private readonly _webviews = new Map(); private readonly _webviewPanels = new Map(); - private readonly _serializers = new Map(); constructor( mainContext: extHostProtocol.IMainContext, @@ -316,24 +310,6 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { return panel; } - public registerWebviewPanelSerializer( - extension: IExtensionDescription, - viewType: string, - serializer: vscode.WebviewPanelSerializer - ): vscode.Disposable { - if (this._serializers.has(viewType)) { - throw new Error(`Serializer for '${viewType}' already registered`); - } - - this._serializers.set(viewType, { serializer, extension }); - this._proxy.$registerSerializer(viewType); - - return new extHostTypes.Disposable(() => { - this._serializers.delete(viewType); - this._proxy.$unregisterSerializer(viewType); - }); - } - public $onMessage( handle: extHostProtocol.WebviewPanelHandle, message: any @@ -392,25 +368,6 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { this._webviews.delete(handle); } - async $deserializeWebviewPanel( - webviewHandle: extHostProtocol.WebviewPanelHandle, - viewType: string, - title: string, - state: any, - position: EditorViewColumn, - options: modes.IWebviewOptions & modes.IWebviewPanelOptions - ): Promise { - const entry = this._serializers.get(viewType); - if (!entry) { - throw new Error(`No serializer found for '${viewType}'`); - } - const { serializer, extension } = entry; - - const webview = this.createNewWebview(webviewHandle, options, extension); - const revivedPanel = this.createNewWebviewPanel(webviewHandle, viewType, title, position, options, webview); - await serializer.deserializeWebviewPanel(revivedPanel, state); - } - public createNewWebviewPanel(webviewHandle: string, viewType: string, title: string, position: number, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, webview: ExtHostWebview) { const panel = new ExtHostWebviewPanel(webviewHandle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview); this._webviewPanels.set(webviewHandle, panel); diff --git a/src/vs/workbench/api/common/extHostWebviewSerializer.ts b/src/vs/workbench/api/common/extHostWebviewSerializer.ts new file mode 100644 index 00000000000..274f91f5353 --- /dev/null +++ b/src/vs/workbench/api/common/extHostWebviewSerializer.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as modes from 'vs/editor/common/modes'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview'; +import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; +import type * as vscode from 'vscode'; +import * as extHostProtocol from './extHost.protocol'; +import * as extHostTypes from './extHostTypes'; + +export class ExtHostWebviewSerializer implements extHostProtocol.ExtHostWebviewSerializerShape { + + private readonly _proxy: extHostProtocol.MainThreadWebviewsShape; + + private readonly _serializers = new Map(); + + constructor( + mainContext: extHostProtocol.IMainContext, + private readonly _webviewService: ExtHostWebviews, + ) { + this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadWebviews); + } + + public registerWebviewPanelSerializer( + extension: IExtensionDescription, + viewType: string, + serializer: vscode.WebviewPanelSerializer + ): vscode.Disposable { + if (this._serializers.has(viewType)) { + throw new Error(`Serializer for '${viewType}' already registered`); + } + + this._serializers.set(viewType, { serializer, extension }); + this._proxy.$registerSerializer(viewType); + + return new extHostTypes.Disposable(() => { + this._serializers.delete(viewType); + this._proxy.$unregisterSerializer(viewType); + }); + } + + async $deserializeWebviewPanel( + webviewHandle: extHostProtocol.WebviewPanelHandle, + viewType: string, + title: string, + state: any, + position: EditorViewColumn, + options: modes.IWebviewOptions & modes.IWebviewPanelOptions + ): Promise { + const entry = this._serializers.get(viewType); + if (!entry) { + throw new Error(`No serializer found for '${viewType}'`); + } + const { serializer, extension } = entry; + + const webview = this._webviewService.createNewWebview(webviewHandle, options, extension); + const revivedPanel = this._webviewService.createNewWebviewPanel(webviewHandle, viewType, title, position, options, webview); + await serializer.deserializeWebviewPanel(revivedPanel, state); + } +} diff --git a/src/vs/workbench/test/browser/api/extHostWebview.test.ts b/src/vs/workbench/test/browser/api/extHostWebview.test.ts index 4e2046729d6..f98153b44d5 100644 --- a/src/vs/workbench/test/browser/api/extHostWebview.test.ts +++ b/src/vs/workbench/test/browser/api/extHostWebview.test.ts @@ -13,6 +13,7 @@ import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { NullApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview'; +import { ExtHostWebviewSerializer } from 'vs/workbench/api/common/extHostWebviewSerializer'; import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; import type * as vscode from 'vscode'; import { SingleProxyRPCProtocol } from './testRPCProtocol'; @@ -35,6 +36,8 @@ suite('ExtHostWebview', () => { isExtensionDevelopmentDebug: false, }, undefined, new NullLogService(), NullApiDeprecationService); + const extHostWebviewSerializer = new ExtHostWebviewSerializer(rpcProtocol!, extHostWebviews); + let lastInvokedDeserializer: vscode.WebviewPanelSerializer | undefined = undefined; class NoopSerializer implements vscode.WebviewPanelSerializer { @@ -48,20 +51,20 @@ suite('ExtHostWebview', () => { const serializerA = new NoopSerializer(); const serializerB = new NoopSerializer(); - const serializerARegistration = extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializerA); + const serializerARegistration = extHostWebviewSerializer.registerWebviewPanelSerializer(extension, viewType, serializerA); - await extHostWebviews.$deserializeWebviewPanel('x', viewType, 'title', {}, 0 as EditorViewColumn, {}); + await extHostWebviewSerializer.$deserializeWebviewPanel('x', viewType, 'title', {}, 0 as EditorViewColumn, {}); assert.strictEqual(lastInvokedDeserializer, serializerA); assert.throws( - () => extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializerB), + () => extHostWebviewSerializer.registerWebviewPanelSerializer(extension, viewType, serializerB), 'Should throw when registering two serializers for the same view'); serializerARegistration.dispose(); - extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializerB); + extHostWebviewSerializer.registerWebviewPanelSerializer(extension, viewType, serializerB); - await extHostWebviews.$deserializeWebviewPanel('x', viewType, 'title', {}, 0 as EditorViewColumn, {}); + await extHostWebviewSerializer.$deserializeWebviewPanel('x', viewType, 'title', {}, 0 as EditorViewColumn, {}); assert.strictEqual(lastInvokedDeserializer, serializerB); }); From 04de4655246c9760fd69dcd4dd195f6b2feb74b2 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 20 Aug 2020 16:38:55 -0700 Subject: [PATCH 392/736] Split main thread webview serializer code into own file --- .../api/browser/mainThreadWebview.ts | 84 +++------------ .../browser/mainThreadWebviewSerializer.ts | 102 ++++++++++++++++++ 2 files changed, 118 insertions(+), 68 deletions(-) create mode 100644 src/vs/workbench/api/browser/mainThreadWebviewSerializer.ts diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index 8d2d1a3f72e..50c90d6e400 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -25,6 +25,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IProductService } from 'vs/platform/product/common/productService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo'; +import { MainThreadWebviewSerializers } from 'vs/workbench/api/browser/mainThreadWebviewSerializer'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; import { IEditorInput, IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor'; @@ -104,7 +105,7 @@ const enum ModelType { Text, } -const webviewPanelViewType = new WebviewViewTypeTransformer('mainThreadWebview-'); +export const webviewPanelViewType = new WebviewViewTypeTransformer('mainThreadWebview-'); @extHostNamedCustomer(extHostProtocol.MainContext.MainThreadWebviews) export class MainThreadWebviews extends Disposable implements extHostProtocol.MainThreadWebviewsShape { @@ -118,12 +119,10 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma ]); private readonly _proxy: extHostProtocol.ExtHostWebviewsShape; - private readonly _proxySerializer: extHostProtocol.ExtHostWebviewSerializerShape; private readonly _proxyViews: extHostProtocol.ExtHostWebviewViewsShape; private readonly _proxyCustomEditors: extHostProtocol.ExtHostCustomEditorsShape; private readonly _webviewInputs = new WebviewInputStore(); - private readonly _revivers = new Map(); private readonly _webviewViewProviders = new Map(); private readonly _webviewViews = new Map(); @@ -131,6 +130,8 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma private readonly _editorProviders = new Map(); private readonly _webviewFromDiffEditorHandles = new Set(); + private readonly serializers: MainThreadWebviewSerializers; + constructor( context: extHostProtocol.IExtHostContext, @IExtensionService extensionService: IExtensionService, @@ -149,8 +150,9 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma ) { super(); + this.serializers = new MainThreadWebviewSerializers(this, context, extensionService, _editorGroupService, _webviewWorkbenchService); + this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviews); - this._proxySerializer = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewSerializer); this._proxyViews = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewViews); this._proxyCustomEditors = context.getProxy(extHostProtocol.ExtHostContext.ExtHostCustomEditors); @@ -167,24 +169,6 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma this.updateWebviewViewStates(this._editorService.activeEditor); })); - // This reviver's only job is to activate extensions. - // This should trigger the real reviver to be registered from the extension host side. - this._register(_webviewWorkbenchService.registerResolver({ - canResolve: (webview: WebviewInput) => { - if (webview instanceof CustomEditorInput) { - extensionService.activateByEvent(`onCustomEditor:${webview.viewType}`); - return false; - } - - const viewType = webviewPanelViewType.toExternal(webview.viewType); - if (typeof viewType === 'string') { - extensionService.activateByEvent(`onWebviewPanel:${viewType}`); - } - return false; - }, - resolveWebview: () => { throw new Error('not implemented'); } - })); - workingCopyFileService.registerWorkingCopyProvider((editorResource) => { const matchedWorkingCopies: IWorkingCopy[] = []; @@ -209,6 +193,11 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma this._editorProviders.clear(); } + public addWebviewInput(handle: extHostProtocol.WebviewPanelHandle, input: WebviewInput): void { + this._webviewInputs.add(handle, input); + this.hookupWebviewEventDelegate(handle, input.webview); + } + public $createWebviewPanel( extensionData: extHostProtocol.WebviewExtensionDescription, handle: extHostProtocol.WebviewPanelHandle, @@ -288,53 +277,12 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma return true; } - public $registerSerializer(viewType: string): void { - if (this._revivers.has(viewType)) { - throw new Error(`Reviver for ${viewType} already registered`); - } - - this._revivers.set(viewType, this._webviewWorkbenchService.registerResolver({ - canResolve: (webviewInput) => { - return webviewInput.viewType === webviewPanelViewType.fromExternal(viewType); - }, - resolveWebview: async (webviewInput): Promise => { - const viewType = webviewPanelViewType.toExternal(webviewInput.viewType); - if (!viewType) { - webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(webviewInput.viewType); - return; - } - - const handle = webviewInput.id; - this._webviewInputs.add(handle, webviewInput); - this.hookupWebviewEventDelegate(handle, webviewInput.webview); - - let state = undefined; - if (webviewInput.webview.state) { - try { - state = JSON.parse(webviewInput.webview.state); - } catch (e) { - console.error('Could not load webview state', e, webviewInput.webview.state); - } - } - - try { - await this._proxySerializer.$deserializeWebviewPanel(handle, viewType, webviewInput.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options); - } catch (error) { - onUnexpectedError(error); - webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(viewType); - } - } - })); + $registerSerializer(viewType: string): void { + this.serializers.$registerSerializer(viewType); } - public $unregisterSerializer(viewType: string): void { - const reviver = this._revivers.get(viewType); - if (!reviver) { - throw new Error(`No reviver for ${viewType} registered`); - } - - reviver.dispose(); - this._revivers.delete(viewType); + $unregisterSerializer(viewType: string): void { + this.serializers.$unregisterSerializer(viewType); } public $registerWebviewViewProvider(viewType: string, options?: { retainContextWhenHidden?: boolean }): void { @@ -657,7 +605,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma return model; } - private static getWebviewResolvedFailedContent(viewType: string) { + public static getWebviewResolvedFailedContent(viewType: string) { return ` diff --git a/src/vs/workbench/api/browser/mainThreadWebviewSerializer.ts b/src/vs/workbench/api/browser/mainThreadWebviewSerializer.ts new file mode 100644 index 00000000000..72b629e524b --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadWebviewSerializer.ts @@ -0,0 +1,102 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { onUnexpectedError } from 'vs/base/common/errors'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { MainThreadWebviews, webviewPanelViewType } from 'vs/workbench/api/browser/mainThreadWebview'; +import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; +import { editorGroupToViewColumn } from 'vs/workbench/api/common/shared/editor'; +import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; +import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; +import { IWebviewWorkbenchService } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; + +export class MainThreadWebviewSerializers extends Disposable { + + private readonly _proxy: extHostProtocol.ExtHostWebviewSerializerShape; + + private readonly _revivers = new Map(); + + constructor( + private readonly mainThreadWebviews: MainThreadWebviews, + context: extHostProtocol.IExtHostContext, + @IExtensionService extensionService: IExtensionService, + @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, + @IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService, + ) { + super(); + + this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewSerializer); + + // This reviver's only job is to activate extensions. + // This should trigger the real reviver to be registered from the extension host side. + this._register(_webviewWorkbenchService.registerResolver({ + canResolve: (webview: WebviewInput) => { + if (webview instanceof CustomEditorInput) { + extensionService.activateByEvent(`onCustomEditor:${webview.viewType}`); + return false; + } + + const viewType = webviewPanelViewType.toExternal(webview.viewType); + if (typeof viewType === 'string') { + extensionService.activateByEvent(`onWebviewPanel:${viewType}`); + } + return false; + }, + resolveWebview: () => { throw new Error('not implemented'); } + })); + } + + public $registerSerializer(viewType: string): void { + if (this._revivers.has(viewType)) { + throw new Error(`Reviver for ${viewType} already registered`); + } + + this._revivers.set(viewType, this._webviewWorkbenchService.registerResolver({ + canResolve: (webviewInput) => { + return webviewInput.viewType === webviewPanelViewType.fromExternal(viewType); + }, + resolveWebview: async (webviewInput): Promise => { + const viewType = webviewPanelViewType.toExternal(webviewInput.viewType); + if (!viewType) { + webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(webviewInput.viewType); + return; + } + + + const handle = webviewInput.id; + + this.mainThreadWebviews.addWebviewInput(handle, webviewInput); + + let state = undefined; + if (webviewInput.webview.state) { + try { + state = JSON.parse(webviewInput.webview.state); + } catch (e) { + console.error('Could not load webview state', e, webviewInput.webview.state); + } + } + + try { + await this._proxy.$deserializeWebviewPanel(handle, viewType, webviewInput.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options); + } catch (error) { + onUnexpectedError(error); + webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(viewType); + } + } + })); + } + + public $unregisterSerializer(viewType: string): void { + const reviver = this._revivers.get(viewType); + if (!reviver) { + throw new Error(`No reviver for ${viewType} registered`); + } + + reviver.dispose(); + this._revivers.delete(viewType); + } +} From c85c46579e95cc5090618446c6fb16edc63419e2 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 20 Aug 2020 16:45:31 -0700 Subject: [PATCH 393/736] Pick up TS 4.0.2 --- 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 4baafc4837c..97870c8d51b 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": "^4.0.2-insiders.20200818" + "typescript": "4.0.2" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 24b3bceafc9..8ed194dd356 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@^4.0.2-insiders.20200818: - version "4.0.2-insiders.20200818" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2-insiders.20200818.tgz#50d949f8d7ae460ce2f2f5a4121ff020a54711e8" - integrity sha512-5/y/W/EFwly+UAm8X77bPjh8FG1GsLRRtrWApIIAsT6kcN9auKhHyrEIUYSahm/WX9F3f7svRxKm6eddvEzyAw== +typescript@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2" + integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ== From 88a7d66e28f09085d4a486fc1e634601acb97991 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 20 Aug 2020 16:52:19 -0700 Subject: [PATCH 394/736] build text diff editor --- .../editor/browser/widget/diffEditorWidget.ts | 19 + .../celllDiffViewModel.ts} | 20 +- .../contrib/notebook/browser/diff/common.ts | 12 + .../notebook/browser/diff/notebookDiff.css | 73 +++ .../browser/diff/notebookTextDiffEditor.ts | 267 +++++++++ .../browser/diff/notebookTextDiffList.ts | 521 ++++++++++++++++++ .../notebook/browser/notebook.contribution.ts | 6 +- 7 files changed, 904 insertions(+), 14 deletions(-) rename src/vs/workbench/contrib/notebook/browser/{media/notebookDiff.css => diff/celllDiffViewModel.ts} (51%) create mode 100644 src/vs/workbench/contrib/notebook/browser/diff/common.ts create mode 100644 src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css create mode 100644 src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts create mode 100644 src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index 9411b1e544b..4925dfcd46c 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -175,6 +175,9 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE private readonly _onDidUpdateDiff: Emitter = this._register(new Emitter()); public readonly onDidUpdateDiff: Event = this._onDidUpdateDiff.event; + private readonly _onDidContentSizeChange: Emitter = this._register(new Emitter()); + public readonly onDidContentSizeChange: Event = this._onDidContentSizeChange.event; + private readonly id: number; private _state: editorBrowser.DiffEditorState; private _updatingDiffProgress: IProgressRunner | null; @@ -421,6 +424,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE return this._renderIndicators; } + public getContentHeight(): number { + return this.modifiedEditor.getContentHeight(); + } + private _setState(newState: editorBrowser.DiffEditorState): void { if (this._state === newState) { return; @@ -555,6 +562,18 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } })); + this._register(editor.onDidContentSizeChange(e => { + const width = this.originalEditor.getContentWidth() + this.modifiedEditor.getContentWidth(); + const height = this.modifiedEditor.getContentHeight(); + + this._onDidContentSizeChange.fire({ + contentHeight: height, + contentWidth: width, + contentHeightChanged: e.contentHeightChanged, + contentWidthChanged: e.contentWidthChanged + }); + })); + return editor; } diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebookDiff.css b/src/vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel.ts similarity index 51% rename from src/vs/workbench/contrib/notebook/browser/media/notebookDiff.css rename to src/vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel.ts index 80404d79179..67392c463ae 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebookDiff.css +++ b/src/vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel.ts @@ -3,15 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.notebook-diff-editor { - display: flex; - flex-direction: row; - height: 100%; - width: 100%; -} -.notebook-diff-editor-modified, -.notebook-diff-editor-original { - display: flex; - height: 100%; - width: 50%; +import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; + +export class CellDiffViewModel { + constructor( + readonly original: NotebookCellTextModel | undefined, + readonly modified: NotebookCellTextModel | undefined, + readonly type: 'unchanged' | 'insert' | 'delete' | 'modified' + ) { + } } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/common.ts b/src/vs/workbench/contrib/notebook/browser/diff/common.ts new file mode 100644 index 00000000000..9e769d6eda7 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/diff/common.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellDiffViewModel } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; + +export interface INotebookTextDiffEditor { + getLayoutInfo(): NotebookLayoutInfo; + layoutNotebookCell(cell: CellDiffViewModel, height: number): void; +} diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css new file mode 100644 index 00000000000..6f4897cf5ea --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* .notebook-diff-editor { + display: flex; + flex-direction: row; + height: 100%; + width: 100%; +} +.notebook-diff-editor-modified, +.notebook-diff-editor-original { + display: flex; + height: 100%; + width: 50%; +} */ + +.notebook-text-diff-editor .cell-diff-editor-container { + margin: 8px; +} + +.notebook-text-diff-editor .cell-diff-editor-container .metadata-container { + display: flex; + height: 24px; + align-items: center; + cursor: default; +} + +.notebook-text-diff-editor .cell-diff-editor-container .metadata-container .metadata-folding-indicator .codicon { + visibility: visible; + padding: 4px 0 0 10px; + cursor: pointer; +} + +.notebook-text-diff-editor .cell-diff-editor-container .metadata-container .metadata-status { + font-size: 12px; +} + +.notebook-text-diff-editor .cell-diff-editor-container .metadata-container .metadata-status span { + margin: 0 8px; + line-height: 21px; +} + +.notebook-text-diff-editor .cell-diff-editor-container.delete .editor-container { + display: inline-block; + width: calc(50% - 18px); +} + +.notebook-text-diff-editor .cell-diff-editor-container.delete .diagonal-fill { + display: inline-block; + width: calc(50% + 18px); +} + +.notebook-text-diff-editor .cell-diff-editor-container.insert .editor-container { + display: inline-block; + width: calc(50% + 18px); +} + +.notebook-text-diff-editor .cell-diff-editor-container.insert .diagonal-fill { + display: inline-block; + width: calc(50% - 18px); +} + +.notebook-text-diff-editor { + overflow: hidden; +} + +.monaco-workbench .notebook-text-diff-editor > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row, +.monaco-workbench .notebook-text-diff-editor > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover, +.monaco-workbench .notebook-text-diff-editor > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused { + outline: none !important; +} diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts new file mode 100644 index 00000000000..574a8338367 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -0,0 +1,267 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import * as DOM from 'vs/base/browser/dom'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorOptions } from 'vs/workbench/common/editor'; +import { notebookCellBorder, NotebookEditorWidget, notebookOutputContainerColor } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; +import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { NotebookDiffEditorInput } from '../notebookDiffEditorInput'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { WorkbenchList } from 'vs/platform/list/browser/listService'; +import { CellDiffViewModel } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { CellDiffRenderer, NotebookCellTextDiffListDelegate, NotebookTextDiffList } from 'vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { diffDiagonalFill, editorBackground, focusBorder, foreground } from 'vs/platform/theme/common/colorRegistry'; +import { INotebookEditorWorkerService } from 'vs/workbench/contrib/notebook/common/services/notebookWorkerService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; +import { getZoomLevel } from 'vs/base/browser/browser'; +import { NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/common'; + +export class NotebookTextDiffEditor extends BaseEditor implements INotebookTextDiffEditor { + static readonly ID: string = 'workbench.editor.notebookTextDiffEditor'; + + private _rootElement!: HTMLElement; + private _dimension: DOM.Dimension | null = null; + private _list!: WorkbenchList; + private _fontInfo: BareFontInfo | undefined; + + constructor( + @IInstantiationService readonly instantiationService: IInstantiationService, + @IThemeService readonly themeService: IThemeService, + @IContextKeyService readonly contextKeyService: IContextKeyService, + @INotebookEditorWorkerService readonly notebookEditorWorkerService: INotebookEditorWorkerService, + @IConfigurationService private readonly configurationService: IConfigurationService, + + @ITelemetryService telemetryService: ITelemetryService, + @IStorageService storageService: IStorageService, + ) { + super(NotebookTextDiffEditor.ID, telemetryService, themeService, storageService); + const editorOptions = this.configurationService.getValue('editor'); + this._fontInfo = BareFontInfo.createFromRawSettings(editorOptions, getZoomLevel()); + } + + protected createEditor(parent: HTMLElement): void { + this._rootElement = DOM.append(parent, DOM.$('.notebook-text-diff-editor')); + + const renderer = this.instantiationService.createInstance(CellDiffRenderer, this); + + this._list = this.instantiationService.createInstance( + NotebookTextDiffList, + 'NotebookTextDiff', + this._rootElement, + this.instantiationService.createInstance(NotebookCellTextDiffListDelegate), + [ + renderer + ], + this.contextKeyService, + { + setRowLineHeight: false, + setRowHeight: false, + supportDynamicHeights: true, + horizontalScrolling: false, + keyboardSupport: false, + mouseSupport: true, + multipleSelectionSupport: false, + enableKeyboardNavigation: true, + additionalScrollHeight: 0, + // transformOptimization: (isMacintosh && isNative) || getTitleBarStyle(this.configurationService, this.environmentService) === 'native', + styleController: (_suffix: string) => { return this._list!; }, + overrideStyles: { + listBackground: editorBackground, + listActiveSelectionBackground: editorBackground, + listActiveSelectionForeground: foreground, + listFocusAndSelectionBackground: editorBackground, + listFocusAndSelectionForeground: foreground, + listFocusBackground: editorBackground, + listFocusForeground: foreground, + listHoverForeground: foreground, + listHoverBackground: editorBackground, + listHoverOutline: focusBorder, + listFocusOutline: focusBorder, + listInactiveSelectionBackground: editorBackground, + listInactiveSelectionForeground: foreground, + listInactiveFocusBackground: editorBackground, + listInactiveFocusOutline: editorBackground, + }, + accessibilityProvider: { + getAriaLabel() { return null; }, + getWidgetAriaLabel() { + return nls.localize('notebookTreeAriaLabel', "Notebook Text Diff"); + } + }, + // focusNextPreviousDelegate: { + // onFocusNext: (applyFocusNext: () => void) => this._updateForCursorNavigationMode(applyFocusNext), + // onFocusPrevious: (applyFocusPrevious: () => void) => this._updateForCursorNavigationMode(applyFocusPrevious), + // } + } + ); + } + + async setInput(input: NotebookDiffEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { + // const group = this.group!; + + await super.setInput(input, options, token); + + const model = await input.resolve(); + if (model === null) { + return; + } + + const diffResult = await this.notebookEditorWorkerService.computeDiff(model.original.resource, model.modified.resource); + const cellChanges = diffResult.cellsDiff.changes; + + const cellDiffViewModels: CellDiffViewModel[] = []; + const originalModel = model.original.notebook; + const modifiedModel = model.modified.notebook; + let originalCellIndex = 0; + let modifiedCellIndex = 0; + + for (let i = 0; i < cellChanges.length; i++) { + const change = cellChanges[i]; + // common cells + + cellDiffViewModels.push(...originalModel.cells.slice(originalCellIndex, change.originalStart).map(cell => { + return new CellDiffViewModel( + cell, + undefined, + 'unchanged' + ); + })); + + // modified cells + const modifiedLen = Math.min(change.originalLength, change.modifiedLength); + + for (let j = 0; j < modifiedLen; j++) { + cellDiffViewModels.push(new CellDiffViewModel( + originalModel.cells[change.originalStart + j], + modifiedModel.cells[change.modifiedStart + j], + 'modified' + )); + } + + for (let j = modifiedLen; j < change.originalLength; j++) { + // deletion + cellDiffViewModels.push(new CellDiffViewModel( + originalModel.cells[change.originalStart + j], + undefined, + 'delete' + )); + } + + for (let j = modifiedLen; j < change.modifiedLength; j++) { + // insertion + cellDiffViewModels.push(new CellDiffViewModel( + undefined, + modifiedModel.cells[change.modifiedStart + j], + 'insert' + )); + } + + originalCellIndex = change.originalStart + change.originalLength; + modifiedCellIndex = change.modifiedStart + change.modifiedLength; + } + + for (let i = originalCellIndex; i < originalModel.cells.length; i++) { + cellDiffViewModels.push(new CellDiffViewModel( + originalModel.cells[i], + undefined, + 'delete' + )); + } + + for (let i = modifiedCellIndex; i < modifiedModel.cells.length; i++) { + cellDiffViewModels.push(new CellDiffViewModel( + undefined, + modifiedModel.cells[i], + 'insert' + )); + } + + this._list.splice(0, this._list.length, cellDiffViewModels); + } + + layoutNotebookCell(cell: CellDiffViewModel, height: number) { + const index = this._list!.indexOf(cell); + if (index >= 0) { + this._list!.updateElementHeight(index, height); + } + } + + getDomNode() { + return this._rootElement; + } + + getControl(): NotebookEditorWidget | undefined { + return undefined; + } + + setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void { + super.setEditorVisible(visible, group); + } + + focus() { + super.focus(); + } + + clearInput(): void { + super.clearInput(); + } + + getLayoutInfo(): NotebookLayoutInfo { + if (!this._list) { + throw new Error('Editor is not initalized successfully'); + } + + return { + width: this._dimension!.width, + height: this._dimension!.height, + fontInfo: this._fontInfo! + }; + } + + layout(dimension: DOM.Dimension): void { + this._rootElement.classList.toggle('mid-width', dimension.width < 1000 && dimension.width >= 600); + this._rootElement.classList.toggle('narrow-width', dimension.width < 600); + this._dimension = dimension; + + this._list?.layout(this._dimension.height, this._dimension.width); + } +} + +registerThemingParticipant((theme, collector) => { + const cellBorderColor = theme.getColor(notebookCellBorder); + if (cellBorderColor) { + collector.addRule(`.notebook-text-diff-editor .editor-container { border: 1px solid ${cellBorderColor};}`); + } + + const diffDiagonalFillColor = theme.getColor(diffDiagonalFill); + collector.addRule(` + .notebook-text-diff-editor .diagonal-fill { + background-image: linear-gradient( + -45deg, + ${diffDiagonalFillColor} 12.5%, + #0000 12.5%, #0000 50%, + ${diffDiagonalFillColor} 50%, ${diffDiagonalFillColor} 62.5%, + #0000 62.5%, #0000 100% + ); + background-size: 8px 8px; + } + `); + + const containerBackground = theme.getColor(notebookOutputContainerColor); + if (containerBackground) { + collector.addRule(`.notebook-text-diff-editor .cell-diff-editor-container .metadata-container { background-color: ${containerBackground}; }`); + } + +}); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts new file mode 100644 index 00000000000..387b8306414 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts @@ -0,0 +1,521 @@ +/*--------------------------------------------------------------------------------------------- + * 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/notebookDiff'; +import { getZoomLevel } from 'vs/base/browser/browser'; +import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import * as DOM from 'vs/base/browser/dom'; +import { IListStyles, IStyleController } from 'vs/base/browser/ui/list/listWidget'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IListService, IWorkbenchListOptions, WorkbenchList } from 'vs/platform/list/browser/listService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { CellDiffViewModel } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; +import { INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/common'; +import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; +import { isMacintosh } from 'vs/base/common/platform'; +import { renderCodicons } from 'vs/base/common/codicons'; + +export interface CellDiffRenderTemplate { + readonly container: HTMLElement; + +} + +const fixedDiffEditorOptions: IEditorOptions = { + padding: { + top: 12, + bottom: 12 + }, + scrollBeyondLastLine: false, + scrollbar: { + verticalScrollbarSize: 14, + horizontal: 'auto', + useShadows: true, + verticalHasArrows: false, + horizontalHasArrows: false, + alwaysConsumeMouseWheel: false + }, + renderLineHighlightOnlyWhenFocus: true, + overviewRulerLanes: 0, + selectOnLineNumbers: false, + wordWrap: 'off', + lineNumbers: 'off', + lineDecorationsWidth: 0, + glyphMargin: true, + fixedOverflowWidgets: true, + minimap: { enabled: false }, + renderValidationDecorations: 'on' +}; + +const fixedEditorOptions: IEditorOptions = { + padding: { + top: 12, + bottom: 12 + }, + scrollBeyondLastLine: false, + scrollbar: { + verticalScrollbarSize: 14, + horizontal: 'auto', + useShadows: true, + verticalHasArrows: false, + horizontalHasArrows: false, + alwaysConsumeMouseWheel: false + }, + renderLineHighlightOnlyWhenFocus: true, + overviewRulerLanes: 0, + selectOnLineNumbers: false, + wordWrap: 'off', + lineNumbers: 'off', + lineDecorationsWidth: 0, + glyphMargin: false, + fixedOverflowWidgets: true, + minimap: { enabled: false }, + renderValidationDecorations: 'on' +}; + +export class NotebookCellTextDiffListDelegate implements IListVirtualDelegate { + private readonly lineHeight: number; + + constructor( + @IConfigurationService private readonly configurationService: IConfigurationService + ) { + const editorOptions = this.configurationService.getValue('editor'); + this.lineHeight = BareFontInfo.createFromRawSettings(editorOptions, getZoomLevel()).lineHeight; + } + + getHeight(element: CellDiffViewModel): number { + return 100; + } + + hasDynamicHeight(element: CellDiffViewModel): boolean { + return false; + } + + getTemplateId(element: CellDiffViewModel): string { + return CellDiffRenderer.TEMPLATE_ID; + } +} +export class CellDiffRenderer implements IListRenderer { + static readonly TEMPLATE_ID = 'cell_diff'; + + constructor( + readonly notebookEditor: INotebookTextDiffEditor, + @IInstantiationService protected readonly instantiationService: IInstantiationService + ) { } + + get templateId() { + return CellDiffRenderer.TEMPLATE_ID; + } + + renderTemplate(container: HTMLElement): CellDiffRenderTemplate { + return { + container + }; + } + + renderElement(element: CellDiffViewModel, index: number, templateData: CellDiffRenderTemplate, height: number | undefined): void { + templateData.container.innerText = ''; + switch (element.type) { + case 'unchanged': + this.instantiationService.createInstance(UnchangedCell, this.notebookEditor, element, templateData); + return; + case 'delete': + this.instantiationService.createInstance(DeletedCell, this.notebookEditor, element, templateData); + return; + case 'insert': + this.instantiationService.createInstance(InsertCell, this.notebookEditor, element, templateData); + return; + case 'modified': + this.instantiationService.createInstance(ModifiedCell, this.notebookEditor, element, templateData); + return; + default: + break; + } + } + + disposeTemplate(templateData: CellDiffRenderTemplate): void { + templateData.container.innerText = ''; + } +} + +class UnchangedCell extends Disposable { + private _editor!: CodeEditorWidget; + constructor( + readonly notebookEditor: INotebookTextDiffEditor, + readonly cell: CellDiffViewModel, + readonly templateData: CellDiffRenderTemplate, + @IInstantiationService protected readonly instantiationService: IInstantiationService, + ) { + super(); + const diffEditorContainer = DOM.$('.cell-diff-editor-container'); + DOM.append(templateData.container, diffEditorContainer); + + const originalCell = cell.original!; + const lineCount = originalCell.textBuffer.getLineCount(); + const lineHeight = notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; + const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; + const editorContainer = DOM.append(diffEditorContainer, DOM.$('.editor-container')); + const metadataContainer = DOM.append(diffEditorContainer, DOM.$('.metadata-container')); + this.buildMetadata(metadataContainer); + + this._editor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, { + ...fixedEditorOptions, + dimension: { + width: notebookEditor.getLayoutInfo().width - 20, + height: editorHeight + } + }, {}); + + this._register(this._editor.onDidContentSizeChange((e) => { + if (e.contentHeightChanged) { + this._editor.layout({ + width: notebookEditor.getLayoutInfo().width - 20, + height: e.contentHeight + }); + this.notebookEditor.layoutNotebookCell(this.cell, e.contentHeight + 32 + 24); + } + })); + + originalCell.resolveTextModelRef().then(ref => { + this._register(ref); + + const textModel = ref.object.textEditorModel; + this._editor.setModel(textModel); + + this._editor.layout({ + width: notebookEditor.getLayoutInfo().width - 20, + height: this._editor.getContentHeight() + }); + this.notebookEditor.layoutNotebookCell(this.cell, this._editor.getContentHeight() + 32 + 24); + }); + } + + buildMetadata(metadataContainer: HTMLElement) { + const foldingIndicator = DOM.append(metadataContainer, DOM.$('.metadata-folding-indicator')); + foldingIndicator.innerHTML = renderCodicons('$(chevron-right)'); + const metadataStatus = DOM.append(metadataContainer, DOM.$('div.metadata-status')); + const metadataStatusSpan = DOM.append(metadataStatus, DOM.$('span')); + metadataStatusSpan.textContent = 'Metadata unchanged'; + + } +} + +class DeletedCell extends Disposable { + private _editor!: CodeEditorWidget; + constructor( + readonly notebookEditor: INotebookTextDiffEditor, + readonly cell: CellDiffViewModel, + readonly templateData: CellDiffRenderTemplate, + @IInstantiationService protected readonly instantiationService: IInstantiationService, + ) { + super(); + const diffEditorContainer = DOM.$('.cell-diff-editor-container'); + DOM.append(templateData.container, diffEditorContainer); + DOM.addClass(diffEditorContainer, 'delete'); + + const originalCell = cell.original!; + const lineCount = originalCell.textBuffer.getLineCount(); + const lineHeight = notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; + const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; + const editorContainer = DOM.append(diffEditorContainer, DOM.$('.editor-container')); + const diagonalFill = DOM.append(diffEditorContainer, DOM.$('.diagonal-fill')); + + this._editor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, { + ...fixedEditorOptions, + dimension: { + width: (notebookEditor.getLayoutInfo().width - 20) / 2 - 18, + height: editorHeight + } + }, {}); + + diagonalFill.style.height = `${editorHeight}px`; + + this._register(this._editor.onDidContentSizeChange((e) => { + if (e.contentHeightChanged) { + this._editor.layout({ + width: (notebookEditor.getLayoutInfo().width - 20) / 2 - 18, + height: e.contentHeight + }); + diagonalFill.style.height = `${e.contentHeight}px`; + this.notebookEditor.layoutNotebookCell(this.cell, e.contentHeight + 32); + } + })); + + originalCell.resolveTextModelRef().then(ref => { + this._register(ref); + + const textModel = ref.object.textEditorModel; + this._editor.setModel(textModel); + + this.notebookEditor.layoutNotebookCell(this.cell, this._editor.getContentHeight() + 32); + diagonalFill.style.height = `${this._editor.getContentHeight()}px`; + + }); + } +} + +class InsertCell extends Disposable { + private _editor!: CodeEditorWidget; + constructor( + readonly notebookEditor: INotebookTextDiffEditor, + readonly cell: CellDiffViewModel, + readonly templateData: CellDiffRenderTemplate, + @IInstantiationService protected readonly instantiationService: IInstantiationService, + ) { + super(); + const diffEditorContainer = DOM.$('.cell-diff-editor-container'); + DOM.append(templateData.container, diffEditorContainer); + DOM.addClass(diffEditorContainer, 'insert'); + + const modifiedCell = cell.modified!; + const lineCount = modifiedCell.textBuffer.getLineCount(); + const lineHeight = notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; + const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; + const diagonalFill = DOM.append(diffEditorContainer, DOM.$('.diagonal-fill')); + const editorContainer = DOM.append(diffEditorContainer, DOM.$('.editor-container')); + + this._editor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, { + ...fixedEditorOptions, + dimension: { + width: (notebookEditor.getLayoutInfo().width - 20) / 2 - 18, + height: editorHeight + } + }, {}); + + diagonalFill.style.height = `${editorHeight}px`; + + this._register(this._editor.onDidContentSizeChange((e) => { + if (e.contentHeightChanged) { + this._editor.layout({ + width: (notebookEditor.getLayoutInfo().width - 20) / 2 - 18, + height: e.contentHeight + }); + diagonalFill.style.height = `${e.contentHeight}px`; + this.notebookEditor.layoutNotebookCell(this.cell, e.contentHeight + 32); + } + })); + + modifiedCell.resolveTextModelRef().then(ref => { + this._register(ref); + + const textModel = ref.object.textEditorModel; + this._editor.setModel(textModel); + + this.notebookEditor.layoutNotebookCell(this.cell, this._editor.getContentHeight() + 32); + diagonalFill.style.height = `${this._editor.getContentHeight()}px`; + }); + } +} + +class ModifiedCell extends Disposable { + private _editor!: DiffEditorWidget; + private _editorContainer!: HTMLElement; + constructor( + readonly notebookEditor: INotebookTextDiffEditor, + readonly cell: CellDiffViewModel, + readonly templateData: CellDiffRenderTemplate, + @IInstantiationService protected readonly instantiationService: IInstantiationService, + ) { + super(); + const diffEditorContainer = DOM.$('.cell-diff-editor-container'); + DOM.append(templateData.container, diffEditorContainer); + + const modifiedCell = cell.modified!; + const lineCount = modifiedCell.textBuffer.getLineCount(); + const lineHeight = notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; + const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; + this._editorContainer = DOM.append(diffEditorContainer, DOM.$('.editor-container')); + + this._editor = this.instantiationService.createInstance(DiffEditorWidget, this._editorContainer, { + ...fixedDiffEditorOptions + }); + + this._editor.layout({ + width: notebookEditor.getLayoutInfo().width - 20, + height: editorHeight + }); + + this._editorContainer.style.height = `${editorHeight}px`; + + this._register(this._editor.onDidContentSizeChange((e) => { + if (e.contentHeightChanged) { + this._editorContainer.style.height = `${e.contentHeight}px`; + this.notebookEditor.layoutNotebookCell(this.cell, e.contentHeight + 32); + this._editor.layout(); + } + })); + + this.initialize(); + } + + async initialize() { + const originalCell = this.cell.original!; + const modifiedCell = this.cell.modified!; + + const originalRef = await originalCell.resolveTextModelRef(); + const modifiedRef = await modifiedCell.resolveTextModelRef(); + const textModel = originalRef.object.textEditorModel; + const modifiedTextModel = modifiedRef.object.textEditorModel; + this._register(originalRef); + this._register(modifiedRef); + + this._editor.setModel({ + original: textModel, + modified: modifiedTextModel + }); + + const contentHeight = this._editor.getContentHeight(); + + this._editorContainer.style.height = `${contentHeight}px`; + this._editor.layout(); + this.notebookEditor.layoutNotebookCell(this.cell, contentHeight + 32); + } +} + +export class NotebookTextDiffList extends WorkbenchList implements IDisposable, IStyleController { + private styleElement?: HTMLStyleElement; + + constructor( + listUser: string, + container: HTMLElement, + delegate: IListVirtualDelegate, + renderers: IListRenderer[], + contextKeyService: IContextKeyService, + options: IWorkbenchListOptions, + @IListService listService: IListService, + @IThemeService themeService: IThemeService, + @IConfigurationService configurationService: IConfigurationService, + @IKeybindingService keybindingService: IKeybindingService, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(listUser, container, delegate, renderers, options, contextKeyService, listService, themeService, configurationService, keybindingService); + } + + style(styles: IListStyles) { + const selectorSuffix = this.view.domId; + if (!this.styleElement) { + this.styleElement = DOM.createStyleSheet(this.view.domNode); + } + const suffix = selectorSuffix && `.${selectorSuffix}`; + const content: string[] = []; + + if (styles.listBackground) { + if (styles.listBackground.isOpaque()) { + content.push(`.monaco-list${suffix} > div.monaco-scrollable-element > .monaco-list-rows { background: ${styles.listBackground}; }`); + } else if (!isMacintosh) { // subpixel AA doesn't exist in macOS + console.warn(`List with id '${selectorSuffix}' was styled with a non-opaque background color. This will break sub-pixel antialiasing.`); + } + } + + if (styles.listFocusBackground) { + content.push(`.monaco-list${suffix}:focus > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused { background-color: ${styles.listFocusBackground}; }`); + content.push(`.monaco-list${suffix}:focus > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused:hover { background-color: ${styles.listFocusBackground}; }`); // overwrite :hover style in this case! + } + + if (styles.listFocusForeground) { + content.push(`.monaco-list${suffix}:focus > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused { color: ${styles.listFocusForeground}; }`); + } + + if (styles.listActiveSelectionBackground) { + content.push(`.monaco-list${suffix}:focus > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.selected { background-color: ${styles.listActiveSelectionBackground}; }`); + content.push(`.monaco-list${suffix}:focus > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.selected:hover { background-color: ${styles.listActiveSelectionBackground}; }`); // overwrite :hover style in this case! + } + + if (styles.listActiveSelectionForeground) { + content.push(`.monaco-list${suffix}:focus > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.selected { color: ${styles.listActiveSelectionForeground}; }`); + } + + if (styles.listFocusAndSelectionBackground) { + content.push(` + .monaco-drag-image, + .monaco-list${suffix}:focus > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.selected.focused { background-color: ${styles.listFocusAndSelectionBackground}; } + `); + } + + if (styles.listFocusAndSelectionForeground) { + content.push(` + .monaco-drag-image, + .monaco-list${suffix}:focus > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.selected.focused { color: ${styles.listFocusAndSelectionForeground}; } + `); + } + + if (styles.listInactiveFocusBackground) { + content.push(`.monaco-list${suffix} > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused { background-color: ${styles.listInactiveFocusBackground}; }`); + content.push(`.monaco-list${suffix} > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused:hover { background-color: ${styles.listInactiveFocusBackground}; }`); // overwrite :hover style in this case! + } + + if (styles.listInactiveSelectionBackground) { + content.push(`.monaco-list${suffix} > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.selected { background-color: ${styles.listInactiveSelectionBackground}; }`); + content.push(`.monaco-list${suffix} > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.selected:hover { background-color: ${styles.listInactiveSelectionBackground}; }`); // overwrite :hover style in this case! + } + + if (styles.listInactiveSelectionForeground) { + content.push(`.monaco-list${suffix} > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.selected { color: ${styles.listInactiveSelectionForeground}; }`); + } + + if (styles.listHoverBackground) { + content.push(`.monaco-list${suffix}:not(.drop-target) > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover:not(.selected):not(.focused) { background-color: ${styles.listHoverBackground}; }`); + } + + if (styles.listHoverForeground) { + content.push(`.monaco-list${suffix} > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover:not(.selected):not(.focused) { color: ${styles.listHoverForeground}; }`); + } + + if (styles.listSelectionOutline) { + content.push(`.monaco-list${suffix} > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.selected { outline: 1px dotted ${styles.listSelectionOutline}; outline-offset: -1px; }`); + } + + if (styles.listFocusOutline) { + content.push(` + .monaco-drag-image, + .monaco-list${suffix}:focus > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; } + `); + } + + if (styles.listInactiveFocusOutline) { + content.push(`.monaco-list${suffix} > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused { outline: 1px dotted ${styles.listInactiveFocusOutline}; outline-offset: -1px; }`); + } + + if (styles.listHoverOutline) { + content.push(`.monaco-list${suffix} > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover { outline: 1px dashed ${styles.listHoverOutline}; outline-offset: -1px; }`); + } + + if (styles.listDropBackground) { + content.push(` + .monaco-list${suffix}.drop-target, + .monaco-list${suffix} > div.monaco-scrollable-element > .monaco-list-rows.drop-target, + .monaco-list${suffix} > div.monaco-scrollable-element > .monaco-list-row.drop-target { background-color: ${styles.listDropBackground} !important; color: inherit !important; } + `); + } + + if (styles.listFilterWidgetBackground) { + content.push(`.monaco-list-type-filter { background-color: ${styles.listFilterWidgetBackground} }`); + } + + if (styles.listFilterWidgetOutline) { + content.push(`.monaco-list-type-filter { border: 1px solid ${styles.listFilterWidgetOutline}; }`); + } + + if (styles.listFilterWidgetNoMatchesOutline) { + content.push(`.monaco-list-type-filter.no-matches { border: 1px solid ${styles.listFilterWidgetNoMatchesOutline}; }`); + } + + if (styles.listMatchesShadow) { + content.push(`.monaco-list-type-filter { box-shadow: 1px 1px 1px ${styles.listMatchesShadow}; }`); + } + + const newStyles = content.join('\n'); + if (newStyles !== this.styleElement.innerHTML) { + this.styleElement.innerHTML = newStyles; + } + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index f80967a2fb1..86382efd0c8 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -43,7 +43,7 @@ import { INotebookEditorModelResolverService, NotebookModelResolverService } fro import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { NotebookDiffEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookDiffEditorInput'; -import { NotebookDiffEditor } from 'vs/workbench/contrib/notebook/browser/notebookDiffEditor'; +import { NotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor'; import { INotebookEditorWorkerService } from 'vs/workbench/contrib/notebook/common/services/notebookWorkerService'; import { NotebookEditorWorkerServiceImpl } from 'vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl'; @@ -79,8 +79,8 @@ Registry.as(EditorExtensions.Editors).registerEditor( Registry.as(EditorExtensions.Editors).registerEditor( EditorDescriptor.create( - NotebookDiffEditor, - NotebookDiffEditor.ID, + NotebookTextDiffEditor, + NotebookTextDiffEditor.ID, 'Notebook Diff Editor' ), [ From 14de6bab5d512f65d02f6b6a0b3a61b1a84d19c7 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 20 Aug 2020 16:54:52 -0700 Subject: [PATCH 395/736] remove unnecessary list view change --- src/vs/base/browser/ui/list/listView.ts | 11 +- src/vs/base/browser/ui/list/listWidget.ts | 3 +- .../browser/diff/notebookTextDiffList.ts | 4 +- .../notebook/browser/notebookDiffEditor.ts | 463 ------------------ .../notebook/browser/view/notebookCellList.ts | 7 +- 5 files changed, 6 insertions(+), 482 deletions(-) delete mode 100644 src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index f97f0b98008..223eb6e437a 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -54,10 +54,6 @@ export interface IListViewOptionsUpdate { readonly horizontalScrolling?: boolean; } -export interface IListViewRangeMapProvider { - (): RangeMap; -} - export interface IListViewOptions extends IListViewOptionsUpdate { readonly dnd?: IListViewDragAndDrop; readonly useShadows?: boolean; @@ -68,7 +64,6 @@ export interface IListViewOptions extends IListViewOptionsUpdate { readonly mouseSupport?: boolean; readonly accessibilityProvider?: IListViewAccessibilityProvider; readonly transformOptimization?: boolean; - readonly rangeMapProvider?: IListViewRangeMapProvider; } const DefaultOptions = { @@ -214,7 +209,6 @@ export class ListView implements ISpliceable, IDisposable { private items: IItem[]; private itemId: number; private rangeMap: RangeMap; - private rangeMapProvider: IListViewRangeMapProvider; private cache: RowCache; private renderers = new Map>(); private lastRenderTop: number; @@ -295,8 +289,7 @@ export class ListView implements ISpliceable, IDisposable { this.items = []; this.itemId = 0; - this.rangeMapProvider = getOrDefault(options, o => o.rangeMapProvider, () => new RangeMap()); - this.rangeMap = this.rangeMapProvider(); + this.rangeMap = new RangeMap(); for (const renderer of renderers) { this.renderers.set(renderer.templateId, renderer); @@ -466,7 +459,7 @@ export class ListView implements ISpliceable, IDisposable { // TODO@joao: improve this optimization to catch even more cases if (start === 0 && deleteCount >= this.items.length) { - this.rangeMap = this.rangeMapProvider(); + this.rangeMap = new RangeMap(); this.rangeMap.splice(0, 0, inserted); this.items = inserted; deleted = []; diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index e5fa1ebe7b9..252150b28d7 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -16,7 +16,7 @@ import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardE 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, ListError, IKeyboardNavigationDelegate } from './list'; -import { ListView, IListViewOptions, IListViewDragAndDrop, IListViewAccessibilityProvider, IListViewOptionsUpdate, IListViewRangeMapProvider } from './listView'; +import { ListView, IListViewOptions, IListViewDragAndDrop, IListViewAccessibilityProvider, IListViewOptionsUpdate } from './listView'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; import { ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable'; @@ -854,7 +854,6 @@ export interface IListOptions { readonly horizontalScrolling?: boolean; readonly additionalScrollHeight?: number; readonly transformOptimization?: boolean; - readonly rangeMapProvider?: IListViewRangeMapProvider; readonly smoothScrolling?: boolean; } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts index 387b8306414..0961206055f 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts @@ -83,13 +83,13 @@ const fixedEditorOptions: IEditorOptions = { }; export class NotebookCellTextDiffListDelegate implements IListVirtualDelegate { - private readonly lineHeight: number; + // private readonly lineHeight: number; constructor( @IConfigurationService private readonly configurationService: IConfigurationService ) { const editorOptions = this.configurationService.getValue('editor'); - this.lineHeight = BareFontInfo.createFromRawSettings(editorOptions, getZoomLevel()).lineHeight; + // this.lineHeight = BareFontInfo.createFromRawSettings(editorOptions, getZoomLevel()).lineHeight; } getHeight(element: CellDiffViewModel): number { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts deleted file mode 100644 index 40ce3828b8e..00000000000 --- a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditor.ts +++ /dev/null @@ -1,463 +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/notebook'; -import 'vs/css!./media/notebookDiff'; -import * as DOM from 'vs/base/browser/dom'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IStorageService } from 'vs/platform/storage/common/storage'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { EditorOptions } from 'vs/workbench/common/editor'; -import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; -import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { NotebookDiffEditorInput } from './notebookDiffEditorInput'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { INotebookDiffEditorModel, INotebookDiffResult } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { INotebookDeltaDecoration } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; -import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; -import { FileService } from 'vs/platform/files/common/fileService'; -import { IFileService } from 'vs/platform/files/common/files'; -import { createDecoration, DECORATIONS, isChangeOrDelete, isChangeOrInsert } from 'vs/editor/browser/widget/diffEditorWidget'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { Color } from 'vs/base/common/color'; -import { IModelDeltaDecoration, IReadonlyTextBuffer } from 'vs/editor/common/model'; -import { Range } from 'vs/editor/common/core/range'; -import { Constants } from 'vs/base/common/uint'; -import { defaultInsertColor, defaultRemoveColor, diffInserted, diffRemoved } from 'vs/platform/theme/common/colorRegistry'; -import { INotebookEditorWorkerService } from 'vs/workbench/contrib/notebook/common/services/notebookWorkerService'; - -export class NotebookDiffEditor extends BaseEditor { - static readonly ID: string = 'workbench.editor.notebookDiffEditor'; - - private _rootElement!: HTMLElement; - private _originalElement!: HTMLElement; - private _modifiedElement!: HTMLElement; - private _dimension?: DOM.Dimension; - private _widget: NotebookEditorWidget | null = null; - private _originalWidget: NotebookEditorWidget | null = null; - private _cellDecorations: string[] = []; - private _originalCellDecorations: string[] = []; - private _strategy!: DiffEditorWidgetSideBySide; - - constructor( - @IFileService private readonly fileService: FileService, - @IThemeService readonly themeService: IThemeService, - @ITelemetryService telemetryService: ITelemetryService, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IStorageService storageService: IStorageService, - @INotebookEditorWorkerService private readonly notebookEditorWorkerService: INotebookEditorWorkerService - ) { - super(NotebookDiffEditor.ID, telemetryService, themeService, storageService); - - } - - protected createEditor(parent: HTMLElement): void { - this._rootElement = DOM.append(parent, DOM.$('.notebook-diff-editor')); - this._originalElement = DOM.append(this._rootElement, DOM.$('.notebook-diff-editor-original')); - this._modifiedElement = DOM.append(this._rootElement, DOM.$('.notebook-diff-editor-modified')); - } - - async setInput(input: NotebookDiffEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { - // const group = this.group!; - - await super.setInput(input, options, token); - this._widget = this.instantiationService.createInstance(NotebookEditorWidget, { isEmbedded: true, contributions: [] }); - this._widget.createEditor(); - - this._originalWidget = this.instantiationService.createInstance(NotebookEditorWidget, { isEmbedded: true, contributions: [] }); - this._originalWidget.createEditor(); - - if (this._dimension) { - this._widget.layout({ - width: this._dimension.width / 2, - height: this._dimension.height - }, this._modifiedElement); - - this._originalWidget.layout({ - width: this._dimension.width / 2, - height: this._dimension.height - }, this._originalElement); - } - - const model = await input.resolve(this._widget.getId()); - - if (model === null) { - return; - } - - model.modified.notebook.metadata.cellEditable = false; - model.modified.notebook.metadata.cellRunnable = false; - model.modified.notebook.metadata.editable = false; - model.modified.notebook.metadata.runnable = false; - model.original.notebook.metadata.cellEditable = false; - model.original.notebook.metadata.cellRunnable = false; - model.original.notebook.metadata.editable = false; - model.original.notebook.metadata.runnable = false; - await this._widget.setModel(model.modified.notebook, undefined); - await this._originalWidget.setModel(model.original.notebook, undefined); - - let widgetScroll = false; - let originalWidgetScroll = false; - this._register(this._widget.onWillScroll(e => { - if (originalWidgetScroll) { - return; - } - widgetScroll = true; - if (this._originalWidget && this._originalWidget.scrollTop !== e.scrollTop) { - this._originalWidget.scrollTop = e.scrollTop; - } - widgetScroll = false; - })); - - this._register(this._originalWidget.onWillScroll(e => { - if (widgetScroll) { - return; - } - - originalWidgetScroll = true; - if (this._widget && this._widget.scrollTop !== e.scrollTop) { - this._widget.scrollTop = e.scrollTop; - } - originalWidgetScroll = false; - })); - - this._register(this.fileService.watch(model.original.resource)); - this._register(this.fileService.onDidFilesChange(async e => { - if (e.changes.find(change => change.resource.toString() === model.original.resource.toString())) { - await model.resolveOriginalFromDisk(); - this._update(model); - } - })); - - this._register(model.modified.notebook.onDidChangeContent(() => { - this._update(model); - })); - - this._register(model.modified.notebook.onDidChangeCells(() => { - this._update(model); - })); - - this._setStrategy(new DiffEditorWidgetSideBySide()); - - this._update(model); - } - - private _update(model: INotebookDiffEditorModel) { - this.notebookEditorWorkerService.computeDiff(model.original.notebook.uri, model.modified.notebook.uri).then(diffResult => { - this._adjustHeight(diffResult); - }); - } - - private _setStrategy(newStrategy: DiffEditorWidgetSideBySide): void { - if (this._strategy) { - this._strategy.dispose(); - } - - this._strategy = newStrategy; - newStrategy.applyColors(this.themeService.getColorTheme()); - } - - private _adjustHeight(notebookDiffResult: INotebookDiffResult) { - const diffResult = notebookDiffResult.cellsDiff; - const linesDiffResult = notebookDiffResult.linesDiff; - - if (!this._widget || !this._originalWidget) { - return; - } - - const originalDecorations: INotebookDeltaDecoration[] = []; - const modifiedDecorations: INotebookDeltaDecoration[] = []; - - let viewLayoutUpdateDisposables: IDisposable[] = []; - - diffResult.changes.forEach(change => { - const originalCells = this._originalWidget?.viewModel?.viewCells.slice(change.originalStart, change.originalStart + change.originalLength) || []; - const original = originalCells.map(cell => cell.handle).map(handle => ({ - handle: handle, - options: { className: 'nb-cell-deleted' } - })); - - const modifiedCells = this._widget?.viewModel?.viewCells.slice(change.modifiedStart, change.modifiedStart + change.modifiedLength) || []; - const modified = modifiedCells.map(cell => cell.handle).map(handle => ({ - handle: handle, - options: { className: 'nb-cell-added' } - })); - - originalDecorations.push(...original); - modifiedDecorations.push(...modified); - - this._originalWidget?.insertWhitespace(change.originalStart + change.originalLength - 1, 0); - this._widget?.insertWhitespace(change.modifiedStart + change.modifiedLength - 1, 0); - - const update = () => { - this._register(DOM.scheduleAtNextAnimationFrame(() => { - const leftTotalHeight = originalCells.map(cell => (cell instanceof CodeCellViewModel) ? cell.layoutInfo.totalHeight : (cell as MarkdownCellViewModel).layoutInfo.totalHeight) - .reduce((p, c) => { return p + c; }, 0); - const rightTotalHeight = modifiedCells.map(cell => (cell instanceof CodeCellViewModel) ? cell.layoutInfo.totalHeight : (cell as MarkdownCellViewModel).layoutInfo.totalHeight) - .reduce((p, c) => { return p + c; }, 0); - const maxHeight = Math.max(leftTotalHeight, rightTotalHeight); - - this._originalWidget?.updateWhitespace(change.originalStart + change.originalLength - 1, maxHeight - leftTotalHeight); - this._widget?.updateWhitespace(change.modifiedStart + change.modifiedLength - 1, maxHeight - rightTotalHeight); - }, 200)); - }; - - viewLayoutUpdateDisposables.push(...[ - ...originalCells.map(cell => (cell instanceof CodeCellViewModel) ? cell.onDidChangeLayout(() => update()) : (cell as MarkdownCellViewModel).onDidChangeLayout(() => update())), - ...modifiedCells.map(cell => (cell instanceof CodeCellViewModel) ? cell.onDidChangeLayout(() => update()) : (cell as MarkdownCellViewModel).onDidChangeLayout(() => update())), - ]); - - update(); - }); - - - linesDiffResult.forEach(diff => { - const originalCell = this._originalWidget!.viewModel!.viewCells.find(cell => cell.handle === diff.originalCellhandle); - const modifiedCell = this._widget!.viewModel!.viewCells.find(cell => cell.handle === diff.modifiedCellhandle); - - if (!originalCell || !modifiedCell) { - return; - } - - const lineDecorations = this._strategy.getEditorsDiffDecorations(diff.lineChanges, false, false, originalCell.textBuffer, modifiedCell.textBuffer); - - this._originalWidget?.changeModelDecorations(accessor => { - accessor.deltaDecorations([], [{ - ownerId: diff.originalCellhandle, - decorations: lineDecorations.original.decorations - }]); - }); - - this._widget?.changeModelDecorations(accessor => { - accessor.deltaDecorations([], [{ - ownerId: diff.modifiedCellhandle, - decorations: lineDecorations.modified.decorations - }]); - }); - }); - - this._originalCellDecorations = this._originalWidget.deltaCellDecorations(this._originalCellDecorations, originalDecorations); - this._cellDecorations = this._widget.deltaCellDecorations(this._cellDecorations, modifiedDecorations); - } - - getDomNode() { - return this._rootElement; - } - - getControl(): NotebookEditorWidget | undefined { - return this._widget || undefined; - } - - setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void { - super.setEditorVisible(visible, group); - if (!visible) { - if (this.input) { - // the widget is not transfered to other editor inputs - this._widget?.onWillHide(); - this._originalWidget?.onWillHide(); - } - } - - } - - focus() { - super.focus(); - this._widget?.focus(); - } - - - clearInput(): void { - this._widget?.onWillHide(); - this._originalWidget?.onWillHide(); - - this._widget?.dispose(); - this._originalWidget?.dispose(); - - this._widget = null; - this._originalWidget = null; - super.clearInput(); - } - - layout(dimension: DOM.Dimension): void { - this._rootElement.classList.toggle('mid-width', dimension.width < 1000 && dimension.width >= 600); - this._rootElement.classList.toggle('narrow-width', dimension.width < 600); - this._dimension = dimension; - - this._widget?.layout({ - width: this._dimension.width / 2, - height: this._dimension.height - }, this._modifiedElement); - - this._originalWidget?.layout({ - width: this._dimension.width / 2, - height: this._dimension.height - }, this._originalElement); - } - -} - -interface IEditorDiffDecorations { - decorations: IModelDeltaDecoration[]; - // overviewZones: OverviewRulerZone[]; -} - -interface IEditorsDiffDecorationsWithZones { - original: IEditorDiffDecorations; - modified: IEditorDiffDecorations; -} - -export class DiffEditorWidgetSideBySide extends Disposable { - private _insertColor: Color | null = null; - private _removeColor: Color | null = null; - - constructor() { - super(); - } - - public applyColors(theme: IColorTheme): boolean { - let newInsertColor = (theme.getColor(diffInserted) || defaultInsertColor).transparent(2); - let newRemoveColor = (theme.getColor(diffRemoved) || defaultRemoveColor).transparent(2); - let hasChanges = !newInsertColor.equals(this._insertColor) || !newRemoveColor.equals(this._removeColor); - this._insertColor = newInsertColor; - this._removeColor = newRemoveColor; - return hasChanges; - } - - public getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalModel: IReadonlyTextBuffer, modifiedModel: IReadonlyTextBuffer): IEditorsDiffDecorationsWithZones { - // Get decorations & overview ruler zones - let originalDecorations = this._getOriginalEditorDecorations(lineChanges, ignoreTrimWhitespace, renderIndicators, originalModel); - let modifiedDecorations = this._getModifiedEditorDecorations(lineChanges, ignoreTrimWhitespace, renderIndicators, modifiedModel); - - return { - original: { - decorations: originalDecorations.decorations, - }, - modified: { - decorations: modifiedDecorations.decorations, - } - }; - } - - protected _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalModel: IReadonlyTextBuffer): IEditorDiffDecorations { - // const overviewZoneColor = String(this._removeColor); - - let result: IEditorDiffDecorations = { - decorations: [], - // overviewZones: [] - }; - - for (let i = 0, length = lineChanges.length; i < length; i++) { - let lineChange = lineChanges[i]; - - if (isChangeOrDelete(lineChange)) { - result.decorations.push({ - range: new Range(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Constants.MAX_SAFE_SMALL_INTEGER), - options: (renderIndicators ? DECORATIONS.lineDeleteWithSign : DECORATIONS.lineDelete) - }); - if (!isChangeOrInsert(lineChange) || !lineChange.charChanges) { - result.decorations.push(createDecoration(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Constants.MAX_SAFE_SMALL_INTEGER, DECORATIONS.charDeleteWholeLine)); - } - - // result.overviewZones.push(new OverviewRulerZone( - // lineChange.originalStartLineNumber, - // lineChange.originalEndLineNumber, - // overviewZoneColor - // )); - - if (lineChange.charChanges) { - for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) { - let charChange = lineChange.charChanges[j]; - if (isChangeOrDelete(charChange)) { - if (ignoreTrimWhitespace) { - for (let lineNumber = charChange.originalStartLineNumber; lineNumber <= charChange.originalEndLineNumber; lineNumber++) { - let startColumn: number; - let endColumn: number; - if (lineNumber === charChange.originalStartLineNumber) { - startColumn = charChange.originalStartColumn; - } else { - startColumn = originalModel.getLineFirstNonWhitespaceColumn(lineNumber); - } - if (lineNumber === charChange.originalEndLineNumber) { - endColumn = charChange.originalEndColumn; - } else { - endColumn = originalModel.getLineLastNonWhitespaceColumn(lineNumber); - } - result.decorations.push(createDecoration(lineNumber, startColumn, lineNumber, endColumn, DECORATIONS.charDelete)); - } - } else { - result.decorations.push(createDecoration(charChange.originalStartLineNumber, charChange.originalStartColumn, charChange.originalEndLineNumber, charChange.originalEndColumn, DECORATIONS.charDelete)); - } - } - } - } - } - } - - return result; - } - - protected _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, modifiedModel: IReadonlyTextBuffer): IEditorDiffDecorations { - // const overviewZoneColor = String(this._insertColor); - - let result: IEditorDiffDecorations = { - decorations: [], - // overviewZones: [] - }; - - - for (let i = 0, length = lineChanges.length; i < length; i++) { - let lineChange = lineChanges[i]; - - if (isChangeOrInsert(lineChange)) { - - result.decorations.push({ - range: new Range(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Constants.MAX_SAFE_SMALL_INTEGER), - options: (renderIndicators ? DECORATIONS.lineInsertWithSign : DECORATIONS.lineInsert) - }); - if (!isChangeOrDelete(lineChange) || !lineChange.charChanges) { - result.decorations.push(createDecoration(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Constants.MAX_SAFE_SMALL_INTEGER, DECORATIONS.charInsertWholeLine)); - } - // result.overviewZones.push(new OverviewRulerZone( - // lineChange.modifiedStartLineNumber, - // lineChange.modifiedEndLineNumber, - // overviewZoneColor - // )); - - if (lineChange.charChanges) { - for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) { - let charChange = lineChange.charChanges[j]; - if (isChangeOrInsert(charChange)) { - if (ignoreTrimWhitespace) { - for (let lineNumber = charChange.modifiedStartLineNumber; lineNumber <= charChange.modifiedEndLineNumber; lineNumber++) { - let startColumn: number; - let endColumn: number; - if (lineNumber === charChange.modifiedStartLineNumber) { - startColumn = charChange.modifiedStartColumn; - } else { - startColumn = modifiedModel.getLineFirstNonWhitespaceColumn(lineNumber); - } - if (lineNumber === charChange.modifiedEndLineNumber) { - endColumn = charChange.modifiedEndColumn; - } else { - endColumn = modifiedModel.getLineLastNonWhitespaceColumn(lineNumber); - } - result.decorations.push(createDecoration(lineNumber, startColumn, lineNumber, endColumn, DECORATIONS.charInsert)); - } - } else { - result.decorations.push(createDecoration(charChange.modifiedStartLineNumber, charChange.modifiedStartColumn, charChange.modifiedEndLineNumber, charChange.modifiedEndColumn, DECORATIONS.charInsert)); - } - } - } - } - - } - } - return result; - } -} diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index da21dbadfec..5dba6bd203a 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -81,12 +81,7 @@ export class NotebookCellList extends WorkbenchList implements ID @IKeybindingService keybindingService: IKeybindingService, @IInstantiationService instantiationService: IInstantiationService ) { - super(listUser, container, delegate, renderers, { - ...options, - rangeMapProvider: () => { - return rangeMap; - } - }, contextKeyService, listService, themeService, configurationService, keybindingService); + super(listUser, container, delegate, renderers, options, contextKeyService, listService, themeService, configurationService, keybindingService); this._rangeMap = rangeMap; NOTEBOOK_CELL_LIST_FOCUSED.bindTo(this.contextKeyService).set(true); this._focusNextPreviousDelegate = options.focusNextPreviousDelegate; From 7475ded21649bedd54c76474d2f6c33524c1d7a2 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 20 Aug 2020 16:56:08 -0700 Subject: [PATCH 396/736] :lipstick: --- .../browser/diff/notebookTextDiffList.ts | 6 +- .../browser/view/rangeMapWithWhitespace.ts | 264 ------------------ .../notebook/test/notebookCommon.test.ts | 170 ----------- 3 files changed, 2 insertions(+), 438 deletions(-) delete mode 100644 src/vs/workbench/contrib/notebook/browser/view/rangeMapWithWhitespace.ts diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts index 0961206055f..5891962d6b2 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts @@ -4,13 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/notebookDiff'; -import { getZoomLevel } from 'vs/base/browser/browser'; import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import * as DOM from 'vs/base/browser/dom'; import { IListStyles, IStyleController } from 'vs/base/browser/ui/list/listWidget'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -86,9 +84,9 @@ export class NotebookCellTextDiffListDelegate implements IListVirtualDelegate('editor'); + // const editorOptions = this.configurationService.getValue('editor'); // this.lineHeight = BareFontInfo.createFromRawSettings(editorOptions, getZoomLevel()).lineHeight; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/rangeMapWithWhitespace.ts b/src/vs/workbench/contrib/notebook/browser/view/rangeMapWithWhitespace.ts deleted file mode 100644 index fdf08269521..00000000000 --- a/src/vs/workbench/contrib/notebook/browser/view/rangeMapWithWhitespace.ts +++ /dev/null @@ -1,264 +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 { consolidate, groupIntersect, IItem, IRangedGroup, RangeMap, shift } from 'vs/base/browser/ui/list/rangeMap'; - -export class ListWhitespace { - - constructor( - public afterIndex: number, - public height: number, - // height of all whitespaces before this whitespace (inclusive) - public prefixSum: number - ) { } -} - -function concat(...groups: IRangedGroup[][]): IRangedGroup[] { - return consolidate(groups.reduce((r, g) => r.concat(g), [])); -} - -// [ { start: 0, len: 10, size: 100 } ] -// [ { start: 0, len: 5, size: 100 }, { start: 5, len: 1, size: 200 }, { start: 6, len: 5, size: 100 } ] - -// [ { afterIndex: 0, height: 20 }, { afterIndex: 2, height: 20 } ] -// 0: 0 -// 1: 100 + 20 -// 2: 100*2 + 20 -// 3: 100*3 + 20 + 20 - -// [ { start: 0, len: 2, size: 2 }, { start: 2, len: 1, size: 3 }, {} ] -export class RangeMapWithWhitespace extends RangeMap { - - private rangeGroups: IRangedGroup[] = []; - private whitespaces: ListWhitespace[] = []; - private _mapSize = 0; - - constructor() { - super(); - } - - splice(index: number, deleteCount: number, items: IItem[] = []): void { - const diff = items.length - deleteCount; - const before = groupIntersect({ start: 0, end: index }, this.rangeGroups); - const after = groupIntersect({ start: index + deleteCount, end: Number.POSITIVE_INFINITY }, this.rangeGroups) - .map(g => ({ range: shift(g.range, diff), size: g.size })); - - const middle = items.map((item, i) => ({ - range: { start: index + i, end: index + i + 1 }, - size: item.size - })); - - this.rangeGroups = concat(before, middle, after); - this._mapSize = this.rangeGroups.reduce((t, g) => t + (g.size * (g.range.end - g.range.start)), 0); - - const deleteRange = deleteCount > 0 ? [index, index + deleteCount - 1] : []; - const indexDelta = items.length - deleteCount; - let prefixSumDelta = 0; - const pendingRemovalWhitespace: number[] = []; - for (let i = 0; i < this.whitespaces.length; i++) { - const whitespace = this.whitespaces[i]; - - if (whitespace.afterIndex < index) { - continue; - } else if (deleteRange.length > 0 && whitespace.afterIndex >= deleteRange[0] && whitespace.afterIndex <= deleteRange[1]) { - // should be deleted - pendingRemovalWhitespace.push(i); - prefixSumDelta += whitespace.height; - } else { - whitespace.afterIndex += indexDelta; - whitespace.prefixSum -= prefixSumDelta; - } - } - - pendingRemovalWhitespace.reverse().forEach(index => { - this.whitespaces.splice(index, 1); - }); - } - - public static findInsertionIndex(arr: ListWhitespace[], afterIndex: number): number { - let low = 0; - let high = arr.length; - - while (low < high) { - const mid = ((low + high) >>> 1); - - if (afterIndex === arr[mid].afterIndex) { - low = mid; - break; - } else if (afterIndex < arr[mid].afterIndex) { - high = mid; - } else { - low = mid + 1; - } - } - - return low; - } - - insertWhitespace(afterIndex: number, height: number) { - // TODO - // 2. delay prefix sum update - const insertIndex = RangeMapWithWhitespace.findInsertionIndex(this.whitespaces, afterIndex); - const prefixSum = insertIndex > 0 ? this.whitespaces[insertIndex - 1].prefixSum + height : height; - const insertedItem = new ListWhitespace(afterIndex, height, prefixSum); - this.whitespaces.splice(insertIndex, 0, insertedItem); - - for (let i = insertIndex + 1; i < this.whitespaces.length; i++) { - this.whitespaces[i].prefixSum += height; - } - } - - // todo, allow multiple whitespaces after one index - updateWhitespace(afterIndex: number, newHeight: number) { - let delta = 0; - let findWhitespace = false; - for (let i = 0; i < this.whitespaces.length; i++) { - if (this.whitespaces[i].afterIndex === afterIndex) { - delta = newHeight - this.whitespaces[i].height; - this.whitespaces[i].height = newHeight; - this.whitespaces[i].prefixSum += delta; - findWhitespace = true; - } else if (this.whitespaces[i].afterIndex > afterIndex) { - if (!findWhitespace) { - this.insertWhitespace(afterIndex, newHeight); - return; - } - this.whitespaces[i].prefixSum += delta; - } - } - - if (!findWhitespace) { - this.insertWhitespace(afterIndex, newHeight); - } - } - - /** - * Returns the number of items in the range map. - */ - get count(): number { - const len = this.rangeGroups.length; - - if (!len) { - return 0; - } - - return this.rangeGroups[len - 1].range.end; - } - - /** - * Returns the sum of the sizes of all items in the range map. - */ - get size(): number { - return this._mapSize - + (this.whitespaces.length ? this.whitespaces[this.whitespaces.length - 1]?.prefixSum : 0); - } - - private _getWhitespaceAccumulatedHeightBeforeIndex(index: number): number { - const lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeIndex(index); - - if (lastWhitespaceBeforeLineNumber === -1) { - return 0; - } - - return this.whitespaces[lastWhitespaceBeforeLineNumber].prefixSum; - } - - private _findLastWhitespaceBeforeIndex(index: number): number { - const arr = this.whitespaces; - let low = 0; - let high = arr.length - 1; - - while (low <= high) { - const delta = (high - low) | 0; - const halfDelta = (delta / 2) | 0; - const mid = (low + halfDelta) | 0; - - if (arr[mid].afterIndex < index) { - if (mid + 1 >= arr.length || arr[mid + 1].afterIndex >= index) { - return mid; - } else { - low = (mid + 1) | 0; - } - } else { - high = (mid - 1) | 0; - } - } - - return -1; - } - - /** - * Returns the index of the item at the given position. - */ - indexAt(position: number): number { - if (position < 0) { - return -1; - } - - let index = 0; - let size = 0; - - for (let group of this.rangeGroups) { - const count = group.range.end - group.range.start; - const newSize = size + (count * group.size); - - if (position < newSize + this._getWhitespaceAccumulatedHeightBeforeIndex(group.range.end + 1)) { - // try to find the right index - let currSize = size; - // position > currSize + all whitespaces before current range - for (let j = group.range.start; j < group.range.end; j++) { - currSize = currSize + group.size; - - if (position >= currSize + this._getWhitespaceAccumulatedHeightBeforeIndex(j + 1)) { - continue; - } else { - return j; - } - } - - return index + Math.floor((position - size) / group.size); - } - - index += count; - size = newSize; - } - - return index; - } - - /** - * Returns the index of the item right after the item at the - * index of the given position. - */ - indexAfter(position: number): number { - return Math.min(this.indexAt(position) + 1, this.count); - } - - /** - * Returns the start position of the item at the given index. - */ - positionAt(index: number): number { - if (index < 0) { - return -1; - } - - let position = 0; - let count = 0; - - for (let group of this.rangeGroups) { - const groupCount = group.range.end - group.range.start; - const newCount = count + groupCount; - - if (index < newCount) { - return position + ((index - count) * group.size) + this._getWhitespaceAccumulatedHeightBeforeIndex(index); - } - - position += groupCount * group.size; - count = newCount; - } - - return -1; - } -} diff --git a/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts b/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts index 27e6ca1d5dc..d092389a66e 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts @@ -8,7 +8,6 @@ import { NOTEBOOK_DISPLAY_ORDER, sortMimeTypes, CellKind, diff, CellUri } from ' import { TestCell, setupInstantiationService } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; import { URI } from 'vs/base/common/uri'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { RangeMapWithWhitespace } from 'vs/workbench/contrib/notebook/browser/view/rangeMapWithWhitespace'; suite('NotebookCommon', () => { const instantiationService = setupInstantiationService(); @@ -356,172 +355,3 @@ suite('CellUri', function () { assert.equal(actual?.notebook.toString(), nb.toString()); }); }); - -suite('RangeMap', () => { - let rangeMap: RangeMapWithWhitespace; - - setup(() => { - rangeMap = new RangeMapWithWhitespace(); - }); - - const one = { size: 1 }; - const two = { size: 2 }; - const three = { size: 3 }; - - test('insert whitespace 1', () => { - rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); - assert.equal(rangeMap.indexAt(0), 0); - assert.equal(rangeMap.indexAt(1), 1); - assert.equal(rangeMap.indexAt(5), 5); - assert.equal(rangeMap.indexAt(9), 9); - assert.equal(rangeMap.indexAt(10), 10); - assert.equal(rangeMap.indexAt(11), 10); - - for (let i = 0; i < 10; i++) { - rangeMap.insertWhitespace(i, 1); - } - - assert.equal(rangeMap.indexAt(0), 0); - assert.equal(rangeMap.indexAt(1), 0); - assert.equal(rangeMap.indexAt(2), 1); - assert.equal(rangeMap.indexAt(3), 1); - assert.equal(rangeMap.indexAt(4), 2); - assert.equal(rangeMap.indexAt(5), 2); - assert.equal(rangeMap.indexAt(6), 3); - assert.equal(rangeMap.indexAt(7), 3); - assert.equal(rangeMap.indexAt(8), 4); - assert.equal(rangeMap.indexAt(9), 4); - }); - - test('insert whitespace 2', () => { - rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); - assert.equal(rangeMap.indexAt(0), 0); - assert.equal(rangeMap.indexAt(1), 1); - assert.equal(rangeMap.indexAt(5), 5); - assert.equal(rangeMap.indexAt(9), 9); - assert.equal(rangeMap.indexAt(10), 10); - assert.equal(rangeMap.indexAt(11), 10); - - rangeMap.insertWhitespace(0, 2); - assert.equal(rangeMap.indexAt(0), 0); - assert.equal(rangeMap.indexAt(1), 0); - assert.equal(rangeMap.indexAt(2), 0); - assert.equal(rangeMap.indexAt(3), 1); - assert.equal(rangeMap.indexAt(4), 2); - assert.equal(rangeMap.indexAt(5), 3); - assert.equal(rangeMap.indexAt(10), 8); - - rangeMap.insertWhitespace(3, 3); - assert.equal(rangeMap.indexAt(0), 0); - assert.equal(rangeMap.indexAt(1), 0); - assert.equal(rangeMap.indexAt(2), 0); - assert.equal(rangeMap.indexAt(3), 1); - assert.equal(rangeMap.indexAt(4), 2); - assert.equal(rangeMap.indexAt(5), 3); - assert.equal(rangeMap.indexAt(6), 3); - assert.equal(rangeMap.indexAt(7), 3); - assert.equal(rangeMap.indexAt(8), 3); - assert.equal(rangeMap.indexAt(9), 4); - assert.equal(rangeMap.indexAt(10), 5); - }); - - test('insert whitespace 3', () => { - rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); - assert.equal(rangeMap.indexAt(0), 0); - assert.equal(rangeMap.indexAt(1), 1); - assert.equal(rangeMap.indexAt(5), 5); - assert.equal(rangeMap.indexAt(9), 9); - assert.equal(rangeMap.indexAt(10), 10); - assert.equal(rangeMap.indexAt(11), 10); - - rangeMap.insertWhitespace(0, 2); - assert.equal(rangeMap.indexAt(0), 0); - assert.equal(rangeMap.indexAt(1), 0); - assert.equal(rangeMap.indexAt(2), 0); - assert.equal(rangeMap.indexAt(3), 1); - assert.equal(rangeMap.indexAt(4), 2); - assert.equal(rangeMap.indexAt(5), 3); - assert.equal(rangeMap.indexAt(10), 8); - - rangeMap.insertWhitespace(3, 3); - rangeMap.updateWhitespace(0, 3); - assert.equal(rangeMap.indexAt(0), 0); - assert.equal(rangeMap.indexAt(1), 0); - assert.equal(rangeMap.indexAt(2), 0); - assert.equal(rangeMap.indexAt(3), 0); - assert.equal(rangeMap.indexAt(4), 1); - assert.equal(rangeMap.indexAt(5), 2); - assert.equal(rangeMap.indexAt(6), 3); - assert.equal(rangeMap.indexAt(7), 3); - assert.equal(rangeMap.indexAt(8), 3); - assert.equal(rangeMap.indexAt(9), 3); - assert.equal(rangeMap.indexAt(10), 4); - }); - - test('insert whitespace, positionAt', () => { - rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); - assert.equal(rangeMap.positionAt(0), 0); - assert.equal(rangeMap.positionAt(1), 1); - assert.equal(rangeMap.positionAt(5), 5); - assert.equal(rangeMap.positionAt(9), 9); - - rangeMap.insertWhitespace(0, 2); - assert.equal(rangeMap.positionAt(0), 0); - assert.equal(rangeMap.positionAt(1), 3); - assert.equal(rangeMap.positionAt(2), 4); - assert.equal(rangeMap.positionAt(3), 5); - }); - - test('insert whitespace, positionAt 2', () => { - rangeMap.splice(0, 0, [one]); - rangeMap.splice(1, 0, [two]); - rangeMap.splice(2, 0, [three]); - assert.equal(rangeMap.positionAt(0), 0); - assert.equal(rangeMap.positionAt(1), 1); - assert.equal(rangeMap.positionAt(2), 3); - - rangeMap.insertWhitespace(0, 2); - assert.equal(rangeMap.positionAt(0), 0); - assert.equal(rangeMap.positionAt(1), 3); - assert.equal(rangeMap.positionAt(2), 5); - }); - - - test('update whitespace when range map splices', () => { - rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); - - rangeMap.insertWhitespace(1, 2); - rangeMap.insertWhitespace(5, 3); - assert.equal(rangeMap.positionAt(0), 0); - assert.equal(rangeMap.positionAt(1), 1); - assert.equal(rangeMap.positionAt(2), 4); - assert.equal(rangeMap.positionAt(3), 5); - assert.equal(rangeMap.positionAt(4), 6); - assert.equal(rangeMap.positionAt(5), 7); - assert.equal(rangeMap.positionAt(6), 11); - assert.equal(rangeMap.positionAt(7), 12); - assert.equal(rangeMap.positionAt(8), 13); - - assert.equal(rangeMap.indexAt(0), 0); - assert.equal(rangeMap.indexAt(1), 1); - assert.equal(rangeMap.indexAt(2), 1); - assert.equal(rangeMap.indexAt(3), 1); - assert.equal(rangeMap.indexAt(4), 2); - - rangeMap.splice(1, 2); - assert.equal(rangeMap.positionAt(0), 0); - assert.equal(rangeMap.positionAt(1), 1); - assert.equal(rangeMap.positionAt(2), 2); - assert.equal(rangeMap.positionAt(3), 3); - assert.equal(rangeMap.positionAt(4), 7); - assert.equal(rangeMap.positionAt(5), 8); - assert.equal(rangeMap.positionAt(6), 9); - - assert.equal(rangeMap.indexAt(0), 0); - assert.equal(rangeMap.indexAt(1), 1); - assert.equal(rangeMap.indexAt(2), 2); - assert.equal(rangeMap.indexAt(3), 3); - assert.equal(rangeMap.indexAt(4), 3); - }); -}); - From 7cbc86333ad9f095c04ac9950a60c7e5f441e5cd Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 20 Aug 2020 17:05:31 -0700 Subject: [PATCH 397/736] :lipstick: --- .../notebook/browser/contrib/scm/scm.ts | 2 +- .../notebook/browser/media/notebook.css | 42 ------ .../notebook/browser/notebookBrowser.ts | 5 +- .../notebook/browser/notebookEditorWidget.ts | 10 -- .../notebook/browser/view/notebookCellList.ts | 29 +--- .../notebook/browser/view/notebookGutter.ts | 126 ------------------ .../common/model/notebookCellTextModel.ts | 1 - 7 files changed, 5 insertions(+), 210 deletions(-) delete mode 100644 src/vs/workbench/contrib/notebook/browser/view/notebookGutter.ts diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts b/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts index 27a45ef2ace..e43a7a8ba74 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts @@ -37,7 +37,7 @@ export class SCMController extends Disposable implements INotebookEditorContribu ) { super(); - if (!this._notebookEditor.creationOptions.isEmbedded) { + if (!this._notebookEditor.isEmbedded) { this._register(this._notebookEditor.onDidChangeModel(() => { this._localDisposable.clear(); this._diffDelayer.cancel(); diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index e5bd4f28407..bcb86a8d99e 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -11,48 +11,6 @@ position: relative; } -.monaco-workbench .notebookOverlay .notebook-gutter { - position: absolute; - width: 6px; - top: 0px; - height: 100%; -} - -.monaco-workbench .notebookOverlay .notebook-gutter .monaco-list-row .cell { - visibility: hidden; - top: -8px; -} - -.monaco-workbench .notebookOverlay .notebook-gutter .monaco-list-row .cell.nb-gutter-cell-changed { - position: relative; - visibility: visible; - width: 2px; - transition: width 80ms linear, left 80ms linear; - background-color: #66afe0; -} - -.monaco-workbench .notebookOverlay .notebook-gutter .monaco-list-row:hover .cell.nb-gutter-cell-changed { - left: 0px; - width: 6px; -} - -.monaco-workbench .notebookOverlay .notebook-gutter .monaco-list-row .cell.nb-gutter-cell-inserted { - position: relative; - visibility: visible; - width: 2px; - transition: width 80ms linear, left 80ms linear; - background-color: #81b88b; -} - -.monaco-workbench .notebookOverlay .notebook-gutter .monaco-list-row:hover .cell.nb-gutter-cell-inserted { - left: 0px; - width: 6px; -} - -.monaco-workbench .notebookOverlay .notebook-gutter .monaco-scrollable-element > .scrollbar{ - visibility: hidden; -} - .monaco-workbench .cell.markdown { user-select: text; -webkit-user-select: text; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 56ceca523b3..d83d8cabf15 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -218,7 +218,6 @@ export interface INotebookEditor extends IEditor { readonly onDidChangeModel: Event; readonly onDidFocusEditorWidget: Event; readonly isNotebookEditor: boolean; - readonly creationOptions: INotebookEditorCreationOptions; activeKernel: INotebookKernelInfo | INotebookKernelInfo2 | undefined; multipleKernelsAvailable: boolean; readonly onDidChangeAvailableKernels: Event; @@ -457,7 +456,7 @@ export interface INotebookEditor extends IEditor { } export interface INotebookCellList { - isDisposed: boolean + isDisposed: boolean; readonly contextKeyService: IContextKeyService; elementAt(position: number): ICellViewModel | undefined; elementHeight(element: ICellViewModel): number; @@ -508,8 +507,6 @@ export interface INotebookCellList { getFocus(): number[]; setFocus(indexes: number[]): void; setSelection(indexes: number[]): void; - insertWhitespace(index: number, height: number): void; - updateWhitespace(index: number, newHeight: number): void; } export interface BaseCellRenderTemplate { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index b7b4d5ce775..fb34fc51874 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -60,7 +60,6 @@ import { isMacintosh, isNative } from 'vs/base/common/platform'; import { getTitleBarStyle } from 'vs/platform/windows/common/windows'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { CellDragAndDropController } from 'vs/workbench/contrib/notebook/browser/view/renderers/dnd'; -import { RangeMapWithWhitespace } from 'vs/workbench/contrib/notebook/browser/view/rangeMapWithWhitespace'; const $ = DOM.$; @@ -446,7 +445,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor onFocusPrevious: (applyFocusPrevious: () => void) => this._updateForCursorNavigationMode(applyFocusPrevious), } }, - new RangeMapWithWhitespace() ); this._dndController.setList(this._list); @@ -1564,14 +1562,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor //#region MISC - insertWhitespace(index: number, height: number) { - this._list?.insertWhitespace(index, height); - } - - updateWhitespace(index: number, newHeight: number) { - this._list?.updateWhitespace(index, newHeight); - } - deltaCellDecorations(oldDecorations: string[], newDecorations: INotebookDeltaDecoration[]): string[] { return this._notebookViewModel?.deltaCellDecorations(oldDecorations, newDecorations) || []; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index 5dba6bd203a..76c404fe169 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -6,7 +6,7 @@ import * as DOM from 'vs/base/browser/dom'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { IListRenderer, IListVirtualDelegate, ListError } from 'vs/base/browser/ui/list/list'; -import { IListStyles, IStyleController, IListOptions } from 'vs/base/browser/ui/list/listWidget'; +import { IListStyles, IStyleController } from 'vs/base/browser/ui/list/listWidget'; import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { isMacintosh } from 'vs/base/common/platform'; @@ -24,8 +24,6 @@ import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/ import { diff, IProcessedOutput, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { clamp } from 'vs/base/common/numbers'; import { SCROLLABLE_ELEMENT_PADDING_TOP } from 'vs/workbench/contrib/notebook/browser/constants'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { RangeMapWithWhitespace } from 'vs/workbench/contrib/notebook/browser/view/rangeMapWithWhitespace'; export interface IFocusNextPreviousDelegate { onFocusNext(applyFocusNext: () => void): void; @@ -64,8 +62,6 @@ export class NotebookCellList extends WorkbenchList implements ID private readonly _focusNextPreviousDelegate: IFocusNextPreviousDelegate; - private _rangeMap: RangeMapWithWhitespace; - constructor( private listUser: string, parentContainer: HTMLElement, @@ -74,15 +70,12 @@ export class NotebookCellList extends WorkbenchList implements ID renderers: IListRenderer[], contextKeyService: IContextKeyService, options: INotebookCellListOptions, - rangeMap: RangeMapWithWhitespace, @IListService listService: IListService, @IThemeService themeService: IThemeService, @IConfigurationService configurationService: IConfigurationService, - @IKeybindingService keybindingService: IKeybindingService, - @IInstantiationService instantiationService: IInstantiationService + @IKeybindingService keybindingService: IKeybindingService ) { super(listUser, container, delegate, renderers, options, contextKeyService, listService, themeService, configurationService, keybindingService); - this._rangeMap = rangeMap; NOTEBOOK_CELL_LIST_FOCUSED.bindTo(this.contextKeyService).set(true); this._focusNextPreviousDelegate = options.focusNextPreviousDelegate; this._previousFocusedElements = this.getFocusedElements(); @@ -383,6 +376,7 @@ export class NotebookCellList extends WorkbenchList implements ID } super.splice(start, deleteCount, elements); + const selectionsLeft = []; this._viewModel!.selectionHandles.forEach(handle => { if (this._viewModel!.hasCell(handle)) { @@ -572,15 +566,6 @@ export class NotebookCellList extends WorkbenchList implements ID this.view.triggerScrollFromMouseWheelEvent(browserEvent); } - insertWhitespace(index: number, height: number) { - this._rangeMap.insertWhitespace(index, height); - this.view.rerender(); - } - - updateWhitespace(index: number, newHeight: number) { - this._rangeMap.updateWhitespace(index, newHeight); - this.view.rerender(); - } updateElementHeight2(element: ICellViewModel, size: number): void { const index = this._getViewIndexUpperBound(element); @@ -828,14 +813,6 @@ export class NotebookCellList extends WorkbenchList implements ID } } - updateOptions(options: IListOptions) { - super.updateOptions(options); - } - - layout(height?: number, width?: number): void { - super.layout(height, width); - } - style(styles: IListStyles) { const selectorSuffix = this.view.domId; diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookGutter.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookGutter.ts deleted file mode 100644 index 798975ef1e6..00000000000 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookGutter.ts +++ /dev/null @@ -1,126 +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 DOM from 'vs/base/browser/dom'; -import { WorkbenchList, IListService, IWorkbenchListOptions } from 'vs/platform/list/browser/listService'; -import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { CellViewModel } from '../viewModel/notebookViewModel'; -import { DisposableStore } from 'vs/base/common/lifecycle'; - - -interface IGutterRendererTemplate { - container: HTMLElement; - cellContainer: HTMLElement; - elementDisposables: DisposableStore; -} - -export class GutterRenderer implements IListRenderer { - - static TEMPLATE_ID = 'notebook_gutter'; - - templateId = 'notebook_gutter'; - - constructor( - private _elementHeightUpdateDelegate: (index: number, size: number) => void - ) { - } - - renderTemplate(container: HTMLElement): IGutterRendererTemplate { - const cellContainer = DOM.append(container, DOM.$('.cell')); - - return { - container, - cellContainer, - elementDisposables: new DisposableStore() - }; - } - - renderElement(element: CellViewModel, index: number, templateData: IGutterRendererTemplate, height: number | undefined): void { - templateData.cellContainer.style.height = `${element.layoutInfo.totalHeight}px`; - let removedClassNames: string[] = []; - templateData.cellContainer.classList.forEach(className => { - if (/^nb\-.*$/.test(className)) { - removedClassNames.push(className); - } - }); - - removedClassNames.forEach(className => { - templateData.cellContainer.classList.remove(className); - }); - - templateData.elementDisposables.add(element.onDidChangeLayout(() => { - templateData.cellContainer.style.height = `${element.layoutInfo.totalHeight}px`; - this._elementHeightUpdateDelegate(index, element.layoutInfo.totalHeight); - })); - - element.getCellDecorations().forEach(options => { - if (options.gutterClassName) { - DOM.addClass(templateData.cellContainer, options.gutterClassName); - } - }); - - templateData.elementDisposables.add(element.onCellDecorationsChanged((e) => { - e.added.forEach(options => { - if (options.gutterClassName) { - DOM.addClass(templateData.cellContainer, options.gutterClassName); - } - }); - - e.removed.forEach(options => { - if (options.gutterClassName) { - DOM.removeClass(templateData.cellContainer, options.gutterClassName); - } - }); - })); - } - disposeTemplate(templateData: IGutterRendererTemplate): void { - templateData.cellContainer.style.backgroundColor = `#fff`; - - templateData.elementDisposables.clear(); - return; - } - - disposeElement(element: CellViewModel, index: number, templateData: IGutterRendererTemplate): void { - templateData.elementDisposables.clear(); - } -} - -export class NotebookGutterDelegate implements IListVirtualDelegate { - getHeight(element: CellViewModel): number { - return element.layoutInfo.totalHeight; - } - - hasDynamicHeight(element: CellViewModel): boolean { - return false; - } - - getTemplateId(element: CellViewModel): string { - return GutterRenderer.TEMPLATE_ID; - } -} - - -export class NotebookGutter extends WorkbenchList { - constructor( - listUser: string, - container: HTMLElement, - delegate: IListVirtualDelegate, - renderers: IListRenderer[], - contextKeyService: IContextKeyService, - options: IWorkbenchListOptions, - @IListService listService: IListService, - @IThemeService themeService: IThemeService, - @IConfigurationService configurationService: IConfigurationService, - @IKeybindingService keybindingService: IKeybindingService, - @IInstantiationService instantiationService: IInstantiationService - ) { - super(listUser, container, delegate, renderers, options, contextKeyService, listService, themeService, configurationService, keybindingService); - } -} diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts index 43356373703..fadae834832 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts @@ -13,7 +13,6 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { hash } from 'vs/base/common/hash'; - export class NotebookCellTextModel extends Disposable implements ICell { private _onDidChangeOutputs = new Emitter(); onDidChangeOutputs: Event = this._onDidChangeOutputs.event; From c24b886d7e6f7e570db1e3b30f0400bd85a2fdc5 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 20 Aug 2020 17:59:00 -0700 Subject: [PATCH 398/736] expand/collapse metadata --- .../contrib/notebook/browser/diff/common.ts | 2 + .../browser/diff/notebookTextDiffEditor.ts | 40 ++++- .../browser/diff/notebookTextDiffList.ts | 167 ++++++++++++++++-- 3 files changed, 188 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/common.ts b/src/vs/workbench/contrib/notebook/browser/diff/common.ts index 9e769d6eda7..e4cb954fbf1 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/common.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/common.ts @@ -5,8 +5,10 @@ import { NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellDiffViewModel } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; +import { Event } from 'vs/base/common/event'; export interface INotebookTextDiffEditor { + onMouseUp: Event<{ readonly event: MouseEvent; readonly target: CellDiffViewModel; }>; getLayoutInfo(): NotebookLayoutInfo; layoutNotebookCell(cell: CellDiffViewModel, height: number): void; } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index 574a8338367..bc979666cfc 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -27,6 +27,8 @@ import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { getZoomLevel } from 'vs/base/browser/browser'; import { NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/common'; +import { Emitter } from 'vs/base/common/event'; +import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; export class NotebookTextDiffEditor extends BaseEditor implements INotebookTextDiffEditor { static readonly ID: string = 'workbench.editor.notebookTextDiffEditor'; @@ -36,6 +38,9 @@ export class NotebookTextDiffEditor extends BaseEditor implements INotebookTextD private _list!: WorkbenchList; private _fontInfo: BareFontInfo | undefined; + private readonly _onMouseUp = this._register(new Emitter<{ readonly event: MouseEvent; readonly target: CellDiffViewModel; }>()); + public readonly onMouseUp = this._onMouseUp.event; + constructor( @IInstantiationService readonly instantiationService: IInstantiationService, @IThemeService readonly themeService: IThemeService, @@ -106,6 +111,12 @@ export class NotebookTextDiffEditor extends BaseEditor implements INotebookTextD // } } ); + + this._register(this._list.onMouseUp(e => { + if (e.element) { + this._onMouseUp.fire({ event: e.browserEvent, target: e.element }); + } + })); } async setInput(input: NotebookDiffEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { @@ -191,11 +202,34 @@ export class NotebookTextDiffEditor extends BaseEditor implements INotebookTextD this._list.splice(0, this._list.length, cellDiffViewModels); } + private pendingLayouts = new WeakMap(); + + layoutNotebookCell(cell: CellDiffViewModel, height: number) { - const index = this._list!.indexOf(cell); - if (index >= 0) { - this._list!.updateElementHeight(index, height); + const relayout = (cell: CellDiffViewModel, height: number) => { + const viewIndex = this._list!.indexOf(cell); + + this._list?.updateElementHeight(viewIndex, height); + }; + + if (this.pendingLayouts.has(cell)) { + this.pendingLayouts.get(cell)!.dispose(); } + + let r: () => void; + const layoutDisposable = DOM.scheduleAtNextAnimationFrame(() => { + this.pendingLayouts.delete(cell); + + relayout(cell, height); + r(); + }); + + this.pendingLayouts.set(cell, toDisposable(() => { + layoutDisposable.dispose(); + r(); + })); + + return new Promise(resolve => { r = resolve; }); } getDomNode() { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts index 5891962d6b2..73bbce8462c 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/notebookDiff'; +import 'vs/css!./notebookDiff'; import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import * as DOM from 'vs/base/browser/dom'; import { IListStyles, IStyleController } from 'vs/base/browser/ui/list/listWidget'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -22,6 +22,8 @@ import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; import { isMacintosh } from 'vs/base/common/platform'; import { renderCodicons } from 'vs/base/common/codicons'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; export interface CellDiffRenderTemplate { readonly container: HTMLElement; @@ -145,15 +147,48 @@ export class CellDiffRenderer implements IListRenderer { if (e.contentHeightChanged) { - this._editor.layout({ - width: notebookEditor.getLayoutInfo().width - 20, - height: e.contentHeight - }); - this.notebookEditor.layoutNotebookCell(this.cell, e.contentHeight + 32 + 24); + this._layoutInfo.editorHeight = e.contentHeight; + this.layout({ editorHeight: true }); } })); @@ -189,21 +222,119 @@ class UnchangedCell extends Disposable { const textModel = ref.object.textEditorModel; this._editor.setModel(textModel); - this._editor.layout({ - width: notebookEditor.getLayoutInfo().width - 20, - height: this._editor.getContentHeight() - }); - this.notebookEditor.layoutNotebookCell(this.cell, this._editor.getContentHeight() + 32 + 24); + this._layoutInfo.editorHeight = this._editor.getContentHeight(); + this.layout({ editorHeight: true }); }); } - buildMetadata(metadataContainer: HTMLElement) { - const foldingIndicator = DOM.append(metadataContainer, DOM.$('.metadata-folding-indicator')); - foldingIndicator.innerHTML = renderCodicons('$(chevron-right)'); - const metadataStatus = DOM.append(metadataContainer, DOM.$('div.metadata-status')); + layout(state: { editorHeight?: boolean, metadataEditor?: boolean }) { + if (state.editorHeight) { + this._editor.layout({ + width: this.notebookEditor.getLayoutInfo().width - 20, + height: this._layoutInfo.editorHeight + }); + } + + if (state.metadataEditor) { + this._metadataEditor?.layout({ + width: this.notebookEditor.getLayoutInfo().width - 20, + height: this._layoutInfo.metadataHeight + }); + } + this.notebookEditor.layoutNotebookCell(this.cell, + this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight); + } + + private _updateFoldingIcon() { + if (this._foldingState === MetadataFoldingState.Collapsed) { + this._foldingIndicator.innerHTML = renderCodicons('$(chevron-right)'); + } else { + this._foldingIndicator.innerHTML = renderCodicons('$(chevron-down)'); + } + } + + buildMetadata() { + this._foldingIndicator = DOM.append(this._metadataHeaderContainer, DOM.$('.metadata-folding-indicator')); + this._updateFoldingIcon(); + const metadataStatus = DOM.append(this._metadataHeaderContainer, DOM.$('div.metadata-status')); const metadataStatusSpan = DOM.append(metadataStatus, DOM.$('span')); metadataStatusSpan.textContent = 'Metadata unchanged'; + this._register(this.notebookEditor.onMouseUp(e => { + if (!e.event.target) { + return; + } + + const target = e.event.target as HTMLElement; + + if (DOM.hasClass(target, 'codicon-chevron-down') || DOM.hasClass(target, 'codicon-chevron-right')) { + const parent = target.parentElement as HTMLElement; + + if (parent && !DOM.hasClass(parent, 'metadata-folding-indicator')) { + return; + } + + // folding icon + + const cellViewModel = e.target; + + if (cellViewModel === this.cell) { + this._foldingState = this._foldingState === MetadataFoldingState.Expanded ? MetadataFoldingState.Collapsed : MetadataFoldingState.Expanded; + this.updateMetadataRendering(); + } + } + + return; + })); + + this.updateMetadataRendering(); + } + + updateMetadataRendering() { + if (this._foldingState === MetadataFoldingState.Expanded) { + // we should expand the metadata editor + this._metadataInfoContainer.style.display = 'block'; + + if (!this._metadataEditorContainer || !this._metadataEditor) { + // create editor + this._metadataEditorContainer = DOM.append(this._metadataInfoContainer, DOM.$('.metadata-editor-container')); + + this._metadataEditor = this.instantiationService.createInstance(CodeEditorWidget, this._metadataEditorContainer, { + ...fixedEditorOptions, + dimension: { + width: this.notebookEditor.getLayoutInfo().width - 20, + height: 0 + } + }, {}); + + const mode = this.modeService.create('json'); + const metadataModel = this.modelService.createModel(JSON.stringify(this.cell.original!.metadata), mode, undefined, true); + this._metadataEditor.setModel(metadataModel); + + this._layoutInfo.metadataHeight = this._metadataEditor.getContentHeight(); + console.log(this._layoutInfo.metadataHeight); + this.layout({ metadataEditor: true }); + + this._register(this._metadataEditor.onDidContentSizeChange((e) => { + if (e.contentHeightChanged && this._foldingState === MetadataFoldingState.Expanded) { + this._layoutInfo.metadataHeight = e.contentHeight; + this.layout({ metadataEditor: true }); + } + })); + } else { + this._layoutInfo.metadataHeight = this._metadataEditor.getContentHeight(); + this.layout({ metadataEditor: true }); + } + } else { + // we should collapse the metadata editor + this._metadataInfoContainer.style.display = 'none'; + this._metadataEditorDisposeStore.clear(); + this._layoutInfo.metadataHeight = 0; + this.layout({}); + } + + this._updateFoldingIcon(); + } } From b5c0ab12563384dd3e37bfbd97626cc6600ffef1 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 20 Aug 2020 18:05:42 -0700 Subject: [PATCH 399/736] avoid editor pane pushing title bar out of view. --- .../contrib/notebook/browser/diff/notebookTextDiffEditor.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index bc979666cfc..75090e8594e 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -268,6 +268,7 @@ export class NotebookTextDiffEditor extends BaseEditor implements INotebookTextD this._rootElement.classList.toggle('mid-width', dimension.width < 1000 && dimension.width >= 600); this._rootElement.classList.toggle('narrow-width', dimension.width < 600); this._dimension = dimension; + this._rootElement.style.height = `${dimension.height}px`; this._list?.layout(this._dimension.height, this._dimension.width); } From 4f2820c28232a051889d9e91b16be24c42fe0516 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 20 Aug 2020 18:09:05 -0700 Subject: [PATCH 400/736] format metadata JSON --- .../notebook/browser/diff/notebookTextDiffEditor.ts | 1 + .../contrib/notebook/browser/diff/notebookTextDiffList.ts | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index 75090e8594e..6a9cdb92f7f 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -278,6 +278,7 @@ registerThemingParticipant((theme, collector) => { const cellBorderColor = theme.getColor(notebookCellBorder); if (cellBorderColor) { collector.addRule(`.notebook-text-diff-editor .editor-container { border: 1px solid ${cellBorderColor};}`); + collector.addRule(`.notebook-text-diff-editor .metadata-editor-container { border: 1px solid ${cellBorderColor};}`); } const diffDiagonalFillColor = theme.getColor(diffDiagonalFill); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts index 73bbce8462c..b16fe0f6686 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts @@ -24,6 +24,8 @@ import { isMacintosh } from 'vs/base/common/platform'; import { renderCodicons } from 'vs/base/common/codicons'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; +import { format } from 'vs/base/common/jsonFormatter'; +import { applyEdits } from 'vs/base/common/jsonEdit'; export interface CellDiffRenderTemplate { readonly container: HTMLElement; @@ -308,7 +310,10 @@ class UnchangedCell extends Disposable { }, {}); const mode = this.modeService.create('json'); - const metadataModel = this.modelService.createModel(JSON.stringify(this.cell.original!.metadata), mode, undefined, true); + const content = JSON.stringify(this.cell.original!.metadata); + const edits = format(content, undefined, {}); + const metadataSource = applyEdits(content, edits); + const metadataModel = this.modelService.createModel(metadataSource, mode, undefined, true); this._metadataEditor.setModel(metadataModel); this._layoutInfo.metadataHeight = this._metadataEditor.getContentHeight(); From a3e008ab60ff7438e1ae9db42e9fbf800b553ffa Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 20 Aug 2020 19:48:20 -0700 Subject: [PATCH 401/736] metadata container border --- .../notebook/browser/diff/notebookTextDiffEditor.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index 6a9cdb92f7f..421f4868d68 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -278,7 +278,16 @@ registerThemingParticipant((theme, collector) => { const cellBorderColor = theme.getColor(notebookCellBorder); if (cellBorderColor) { collector.addRule(`.notebook-text-diff-editor .editor-container { border: 1px solid ${cellBorderColor};}`); - collector.addRule(`.notebook-text-diff-editor .metadata-editor-container { border: 1px solid ${cellBorderColor};}`); + collector.addRule(`.notebook-text-diff-editor .metadata-editor-container { + border-left: 1px solid ${cellBorderColor}; + border-right: 1px solid ${cellBorderColor}; + border-bottom: 1px solid ${cellBorderColor}; + }`); + collector.addRule(`.notebook-text-diff-editor .cell-diff-editor-container .metadata-container { + border-left: 1px solid ${cellBorderColor}; + border-right: 1px solid ${cellBorderColor}; + border-bottom: 1px solid ${cellBorderColor}; + }`); } const diffDiagonalFillColor = theme.getColor(diffDiagonalFill); From 956343798b4718c9946144004ce71366a309abab Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 20 Aug 2020 20:30:35 -0700 Subject: [PATCH 402/736] Set context on delete cell toolbar Fix #105109 --- src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts | 1 + .../contrib/notebook/browser/view/renderers/cellRenderer.ts | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 8374805c4c4..5884856ca6c 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -513,6 +513,7 @@ export interface BaseCellRenderTemplate { container: HTMLElement; cellContainer: HTMLElement; toolbar: ToolBar; + deleteToolbar: ToolBar; betweenCellToolbar: ToolBar; focusIndicatorLeft: HTMLElement; disposables: DisposableStore; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 574f99c36fc..9d02f255038 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -416,6 +416,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR disposables, elementDisposables: new DisposableStore(), toolbar, + deleteToolbar, betweenCellToolbar, bottomCellContainer, statusBarContainer: statusBar.statusBarContainer, @@ -480,6 +481,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR $mid: 12 }; templateData.toolbar.context = toolbarContext; + templateData.deleteToolbar.context = toolbarContext; this.setBetweenCellToolbarContext(templateData, element, toolbarContext); @@ -719,6 +721,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende focusIndicatorRight, focusIndicatorBottom, toolbar, + deleteToolbar, betweenCellToolbar, focusSinkElement, runToolbar, @@ -870,6 +873,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende }; templateData.toolbar.context = toolbarContext; templateData.runToolbar.context = toolbarContext; + templateData.deleteToolbar.context = toolbarContext; this.setBetweenCellToolbarContext(templateData, element, toolbarContext); From bc172e197d1448a3afcc7286dc9b21eab9ab4627 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 20 Aug 2020 21:42:49 -0700 Subject: [PATCH 403/736] Fix language picker hover --- src/vs/workbench/contrib/notebook/browser/media/notebook.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 560a756fe12..fcf4355f7e1 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -340,6 +340,7 @@ .monaco-workbench .notebookOverlay .cell-statusbar-container .cell-status-right { padding-right: 12px; + z-index: 26; } .monaco-workbench .notebookOverlay .cell-statusbar-container .cell-language-picker { From 44ad54d9faa4604ab17628e8396e072258187737 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 21 Aug 2020 07:37:34 +0200 Subject: [PATCH 404/736] Revert "Split main thread webview serializer code into own file" This reverts commit 04de4655246c9760fd69dcd4dd195f6b2feb74b2. --- .../api/browser/mainThreadWebview.ts | 84 ++++++++++++--- .../browser/mainThreadWebviewSerializer.ts | 102 ------------------ 2 files changed, 68 insertions(+), 118 deletions(-) delete mode 100644 src/vs/workbench/api/browser/mainThreadWebviewSerializer.ts diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index 50c90d6e400..8d2d1a3f72e 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -25,7 +25,6 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IProductService } from 'vs/platform/product/common/productService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo'; -import { MainThreadWebviewSerializers } from 'vs/workbench/api/browser/mainThreadWebviewSerializer'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; import { IEditorInput, IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor'; @@ -105,7 +104,7 @@ const enum ModelType { Text, } -export const webviewPanelViewType = new WebviewViewTypeTransformer('mainThreadWebview-'); +const webviewPanelViewType = new WebviewViewTypeTransformer('mainThreadWebview-'); @extHostNamedCustomer(extHostProtocol.MainContext.MainThreadWebviews) export class MainThreadWebviews extends Disposable implements extHostProtocol.MainThreadWebviewsShape { @@ -119,10 +118,12 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma ]); private readonly _proxy: extHostProtocol.ExtHostWebviewsShape; + private readonly _proxySerializer: extHostProtocol.ExtHostWebviewSerializerShape; private readonly _proxyViews: extHostProtocol.ExtHostWebviewViewsShape; private readonly _proxyCustomEditors: extHostProtocol.ExtHostCustomEditorsShape; private readonly _webviewInputs = new WebviewInputStore(); + private readonly _revivers = new Map(); private readonly _webviewViewProviders = new Map(); private readonly _webviewViews = new Map(); @@ -130,8 +131,6 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma private readonly _editorProviders = new Map(); private readonly _webviewFromDiffEditorHandles = new Set(); - private readonly serializers: MainThreadWebviewSerializers; - constructor( context: extHostProtocol.IExtHostContext, @IExtensionService extensionService: IExtensionService, @@ -150,9 +149,8 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma ) { super(); - this.serializers = new MainThreadWebviewSerializers(this, context, extensionService, _editorGroupService, _webviewWorkbenchService); - this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviews); + this._proxySerializer = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewSerializer); this._proxyViews = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewViews); this._proxyCustomEditors = context.getProxy(extHostProtocol.ExtHostContext.ExtHostCustomEditors); @@ -169,6 +167,24 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma this.updateWebviewViewStates(this._editorService.activeEditor); })); + // This reviver's only job is to activate extensions. + // This should trigger the real reviver to be registered from the extension host side. + this._register(_webviewWorkbenchService.registerResolver({ + canResolve: (webview: WebviewInput) => { + if (webview instanceof CustomEditorInput) { + extensionService.activateByEvent(`onCustomEditor:${webview.viewType}`); + return false; + } + + const viewType = webviewPanelViewType.toExternal(webview.viewType); + if (typeof viewType === 'string') { + extensionService.activateByEvent(`onWebviewPanel:${viewType}`); + } + return false; + }, + resolveWebview: () => { throw new Error('not implemented'); } + })); + workingCopyFileService.registerWorkingCopyProvider((editorResource) => { const matchedWorkingCopies: IWorkingCopy[] = []; @@ -193,11 +209,6 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma this._editorProviders.clear(); } - public addWebviewInput(handle: extHostProtocol.WebviewPanelHandle, input: WebviewInput): void { - this._webviewInputs.add(handle, input); - this.hookupWebviewEventDelegate(handle, input.webview); - } - public $createWebviewPanel( extensionData: extHostProtocol.WebviewExtensionDescription, handle: extHostProtocol.WebviewPanelHandle, @@ -277,12 +288,53 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma return true; } - $registerSerializer(viewType: string): void { - this.serializers.$registerSerializer(viewType); + public $registerSerializer(viewType: string): void { + if (this._revivers.has(viewType)) { + throw new Error(`Reviver for ${viewType} already registered`); + } + + this._revivers.set(viewType, this._webviewWorkbenchService.registerResolver({ + canResolve: (webviewInput) => { + return webviewInput.viewType === webviewPanelViewType.fromExternal(viewType); + }, + resolveWebview: async (webviewInput): Promise => { + const viewType = webviewPanelViewType.toExternal(webviewInput.viewType); + if (!viewType) { + webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(webviewInput.viewType); + return; + } + + const handle = webviewInput.id; + this._webviewInputs.add(handle, webviewInput); + this.hookupWebviewEventDelegate(handle, webviewInput.webview); + + let state = undefined; + if (webviewInput.webview.state) { + try { + state = JSON.parse(webviewInput.webview.state); + } catch (e) { + console.error('Could not load webview state', e, webviewInput.webview.state); + } + } + + try { + await this._proxySerializer.$deserializeWebviewPanel(handle, viewType, webviewInput.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options); + } catch (error) { + onUnexpectedError(error); + webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(viewType); + } + } + })); } - $unregisterSerializer(viewType: string): void { - this.serializers.$unregisterSerializer(viewType); + public $unregisterSerializer(viewType: string): void { + const reviver = this._revivers.get(viewType); + if (!reviver) { + throw new Error(`No reviver for ${viewType} registered`); + } + + reviver.dispose(); + this._revivers.delete(viewType); } public $registerWebviewViewProvider(viewType: string, options?: { retainContextWhenHidden?: boolean }): void { @@ -605,7 +657,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma return model; } - public static getWebviewResolvedFailedContent(viewType: string) { + private static getWebviewResolvedFailedContent(viewType: string) { return ` diff --git a/src/vs/workbench/api/browser/mainThreadWebviewSerializer.ts b/src/vs/workbench/api/browser/mainThreadWebviewSerializer.ts deleted file mode 100644 index 72b629e524b..00000000000 --- a/src/vs/workbench/api/browser/mainThreadWebviewSerializer.ts +++ /dev/null @@ -1,102 +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 { onUnexpectedError } from 'vs/base/common/errors'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import { MainThreadWebviews, webviewPanelViewType } from 'vs/workbench/api/browser/mainThreadWebview'; -import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; -import { editorGroupToViewColumn } from 'vs/workbench/api/common/shared/editor'; -import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; -import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; -import { IWebviewWorkbenchService } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; -import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; - -export class MainThreadWebviewSerializers extends Disposable { - - private readonly _proxy: extHostProtocol.ExtHostWebviewSerializerShape; - - private readonly _revivers = new Map(); - - constructor( - private readonly mainThreadWebviews: MainThreadWebviews, - context: extHostProtocol.IExtHostContext, - @IExtensionService extensionService: IExtensionService, - @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, - @IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService, - ) { - super(); - - this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewSerializer); - - // This reviver's only job is to activate extensions. - // This should trigger the real reviver to be registered from the extension host side. - this._register(_webviewWorkbenchService.registerResolver({ - canResolve: (webview: WebviewInput) => { - if (webview instanceof CustomEditorInput) { - extensionService.activateByEvent(`onCustomEditor:${webview.viewType}`); - return false; - } - - const viewType = webviewPanelViewType.toExternal(webview.viewType); - if (typeof viewType === 'string') { - extensionService.activateByEvent(`onWebviewPanel:${viewType}`); - } - return false; - }, - resolveWebview: () => { throw new Error('not implemented'); } - })); - } - - public $registerSerializer(viewType: string): void { - if (this._revivers.has(viewType)) { - throw new Error(`Reviver for ${viewType} already registered`); - } - - this._revivers.set(viewType, this._webviewWorkbenchService.registerResolver({ - canResolve: (webviewInput) => { - return webviewInput.viewType === webviewPanelViewType.fromExternal(viewType); - }, - resolveWebview: async (webviewInput): Promise => { - const viewType = webviewPanelViewType.toExternal(webviewInput.viewType); - if (!viewType) { - webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(webviewInput.viewType); - return; - } - - - const handle = webviewInput.id; - - this.mainThreadWebviews.addWebviewInput(handle, webviewInput); - - let state = undefined; - if (webviewInput.webview.state) { - try { - state = JSON.parse(webviewInput.webview.state); - } catch (e) { - console.error('Could not load webview state', e, webviewInput.webview.state); - } - } - - try { - await this._proxy.$deserializeWebviewPanel(handle, viewType, webviewInput.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options); - } catch (error) { - onUnexpectedError(error); - webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(viewType); - } - } - })); - } - - public $unregisterSerializer(viewType: string): void { - const reviver = this._revivers.get(viewType); - if (!reviver) { - throw new Error(`No reviver for ${viewType} registered`); - } - - reviver.dispose(); - this._revivers.delete(viewType); - } -} From d50e84208f0358b4ee17b32c5394ea5605641b81 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 21 Aug 2020 08:15:37 +0200 Subject: [PATCH 405/736] web - ignore builtIn extension if overridden via CLI --- resources/web/code-web.js | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/resources/web/code-web.js b/resources/web/code-web.js index 5c8046693a6..74dd39e8cb8 100644 --- a/resources/web/code-web.js +++ b/resources/web/code-web.js @@ -144,7 +144,7 @@ async function ensureWebDevExtensions() { } } -async function getDefaultExtensionInfos() { +async function getCommandlineProvidedExtensionInfos() { const extensions = []; /** @type {Object.} */ @@ -196,7 +196,7 @@ async function getExtensionPackageJSON(extensionPath) { } const builtInExtensionsPromise = getBuiltInExtensionInfos(); -const defaultExtensionsPromise = getDefaultExtensionInfos(); +const commandlineProvidedExtensionsPromise = getCommandlineProvidedExtensionInfos(); const mapCallbackUriToRequestId = new Map(); @@ -291,7 +291,7 @@ async function handleStatic(req, res, parsedUrl) { async function handleExtension(req, res, parsedUrl) { // Strip `/extension/` from the path const relativePath = decodeURIComponent(parsedUrl.pathname.substr('/extension/'.length)); - const filePath = getExtensionFilePath(relativePath, (await defaultExtensionsPromise).locations); + const filePath = getExtensionFilePath(relativePath, (await commandlineProvidedExtensionsPromise).locations); if (!filePath) { return serveError(req, res, 400, `Bad request.`); } @@ -335,10 +335,21 @@ async function handleRoot(req, res) { } const { extensions: builtInExtensions } = await builtInExtensionsPromise; - const { extensions: staticExtensions } = await defaultExtensionsPromise; + const { extensions: staticExtensions, locations: staticLocations } = await commandlineProvidedExtensionsPromise; + + const dedupedBuiltInExtensions = []; + for (const builtInExtension of builtInExtensions) { + const extensionId = `${builtInExtension.packageJSON.publisher}.${builtInExtension.packageJSON.name}`; + if (staticLocations[extensionId]) { + fancyLog(`${ansiColors.magenta('BuiltIn extensions')}: Ignoring built-in ${extensionId} because it was overridden via --extension argument`); + continue; + } + + dedupedBuiltInExtensions.push(builtInExtension); + } if (args.verbose) { - fancyLog(`${ansiColors.magenta('BuiltIn extensions')}: ${builtInExtensions.map(e => path.basename(e.extensionPath)).join(', ')}`); + fancyLog(`${ansiColors.magenta('BuiltIn extensions')}: ${dedupedBuiltInExtensions.map(e => path.basename(e.extensionPath)).join(', ')}`); fancyLog(`${ansiColors.magenta('Additional extensions')}: ${staticExtensions.map(e => path.basename(e.extensionLocation.path)).join(', ') || 'None'}`); } @@ -352,7 +363,7 @@ async function handleRoot(req, res) { const data = (await readFile(WEB_MAIN)).toString() .replace('{{WORKBENCH_WEB_CONFIGURATION}}', () => escapeAttribute(JSON.stringify(webConfigJSON))) // use a replace function to avoid that regexp replace patterns ($&, $0, ...) are applied - .replace('{{WORKBENCH_BUILTIN_EXTENSIONS}}', () => escapeAttribute(JSON.stringify(builtInExtensions))) + .replace('{{WORKBENCH_BUILTIN_EXTENSIONS}}', () => escapeAttribute(JSON.stringify(dedupedBuiltInExtensions))) .replace('{{WEBVIEW_ENDPOINT}}', '') .replace('{{REMOTE_USER_DATA_URI}}', ''); From 38f8b223ffd3fb8c76c5911554c21f0dfa2089e6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 21 Aug 2020 08:43:11 +0200 Subject: [PATCH 406/736] web - remoteIndicator => windowIndicator --- src/vs/code/browser/workbench/workbench.ts | 14 +++++++------- .../workbench/browser/workbench.contribution.ts | 2 +- .../contrib/remote/browser/remoteIndicator.ts | 4 ++-- src/vs/workbench/workbench.web.api.ts | 16 ++++++++-------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index ebf75434a9f..cd315f77cc0 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWorkbenchConstructionOptions, create, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IRemoteIndicator, ICommand, IHomeIndicator, IProductQualityChangeHandler } from 'vs/workbench/workbench.web.api'; +import { IWorkbenchConstructionOptions, create, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IWindowIndicator, ICommand, IHomeIndicator, IProductQualityChangeHandler } from 'vs/workbench/workbench.web.api'; import product from 'vs/platform/product/common/product'; import { URI, UriComponents } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; @@ -279,7 +279,7 @@ class WorkspaceProvider implements IWorkspaceProvider { } } -class RemoteIndicator implements IRemoteIndicator { +class WindowIndicator implements IWindowIndicator { readonly onDidChange = Event.None; @@ -401,10 +401,10 @@ class RemoteIndicator implements IRemoteIndicator { // Commands const commands: ICommand[] = []; - // Remote indicator - const remoteIndicator = new RemoteIndicator(workspace); - if (remoteIndicator.commandImpl) { - commands.push(remoteIndicator.commandImpl); + // Window indicator + const windowIndicator = new WindowIndicator(workspace); + if (windowIndicator.commandImpl) { + commands.push(windowIndicator.commandImpl); } // Product Quality Change Handler @@ -427,7 +427,7 @@ class RemoteIndicator implements IRemoteIndicator { ...config, homeIndicator, commands, - remoteIndicator, + windowIndicator, productQualityChangeHandler, workspaceProvider: new WorkspaceProvider(workspace, payload), urlCallbackProvider: new PollingURLCallbackProvider(), diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 7612428bc95..88050424cda 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -309,7 +309,7 @@ import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuratio const base = '${dirty}${activeEditorShort}${separator}${rootName}${separator}${appName}'; if (isWeb) { - return base + '${separator}${remoteName}'; // Web: always show remote indicator + return base + '${separator}${remoteName}'; // Web: always show remote name } return base; diff --git a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts index 63cd68da412..fb51919e9b1 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts @@ -119,7 +119,7 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr this._register(this.labelService.onDidChangeFormatters(() => this.updateRemoteStatusIndicator())); // Update based on remote indicator changes if any - const remoteIndicator = this.environmentService.options?.remoteIndicator; + const remoteIndicator = this.environmentService.options?.windowIndicator; if (remoteIndicator) { this._register(remoteIndicator.onDidChange(() => this.updateRemoteStatusIndicator())); } @@ -188,7 +188,7 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr private updateRemoteStatusIndicator(): void { // Remote indicator: show if provided via options - const remoteIndicator = this.environmentService.options?.remoteIndicator; + const remoteIndicator = this.environmentService.options?.windowIndicator; if (remoteIndicator) { this.renderRemoteStatusIndicator(remoteIndicator.label, remoteIndicator.tooltip, remoteIndicator.command); } diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 9331e328ea3..53612d32cdf 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -121,28 +121,28 @@ interface IHomeIndicator { title: string; } -interface IRemoteIndicator { +interface IWindowIndicator { /** - * Triggering this event will cause the remote indicator to update. + * Triggering this event will cause the window indicator to update. */ onDidChange: Event; /** - * Label of the remote indicator may include octicons + * Label of the window indicator may include octicons * e.g. `$(remote) label` */ label: string; /** - * Tooltip of the remote indicator should not include + * Tooltip of the window indicator should not include * octicons and be descriptive. */ tooltip: string; /** * If provided, overrides the default command that - * is executed when clicking on the remote indicator. + * is executed when clicking on the window indicator. */ command?: string; } @@ -377,9 +377,9 @@ interface IWorkbenchConstructionOptions { readonly productConfiguration?: Partial; /** - * Optional override for properties of the remote window indicator in the status bar. + * Optional override for properties of the window indicator in the status bar. */ - readonly remoteIndicator?: IRemoteIndicator; + readonly windowIndicator?: IWindowIndicator; //#endregion @@ -535,7 +535,7 @@ export { // Branding IHomeIndicator, IProductConfiguration, - IRemoteIndicator, + IWindowIndicator, // Default layout IDefaultView, From 67c90e6930f33509b586570248d50486dc21e3da Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 21 Aug 2020 09:29:34 +0200 Subject: [PATCH 407/736] fuzzy scorer - bring back prefix match boost (fix #103052) --- src/vs/base/common/fuzzyScorer.ts | 55 ++++++++++++++++++--- src/vs/base/test/common/fuzzyScorer.test.ts | 15 ++++++ 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/src/vs/base/common/fuzzyScorer.ts b/src/vs/base/common/fuzzyScorer.ts index 1c8e5ef5fb5..c0c235c6874 100644 --- a/src/vs/base/common/fuzzyScorer.ts +++ b/src/vs/base/common/fuzzyScorer.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { compareAnything } from 'vs/base/common/comparers'; -import { IMatch, isUpper, fuzzyScore, createMatches as createFuzzyMatches } from 'vs/base/common/filters'; +import { matchesPrefix, IMatch, isUpper, fuzzyScore, createMatches as createFuzzyMatches, matchesStrictPrefix } from 'vs/base/common/filters'; import { sep } from 'vs/base/common/path'; import { isWindows, isLinux } from 'vs/base/common/platform'; import { stripWildcards, equalsIgnoreCase } from 'vs/base/common/strings'; @@ -369,7 +369,9 @@ export interface IItemAccessor { } const PATH_IDENTITY_SCORE = 1 << 18; -const LABEL_SCORE_THRESHOLD = 1 << 17; +const LABEL_PREFIX_SCORE_MATCHCASE = 1 << 17; +const LABEL_PREFIX_SCORE_IGNORECASE = 1 << 16; +const LABEL_SCORE_THRESHOLD = 1 << 15; export function scoreItemFuzzy(item: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor, cache: FuzzyScorerCache): IItemScore { if (!item || !query.normalized) { @@ -460,6 +462,15 @@ function doScoreItemFuzzySingle(label: string, description: string | undefined, // Prefer label matches if told so if (preferLabelMatches) { + + // Treat prefix matches on the label highest + const prefixLabelMatchIgnoreCase = matchesPrefix(query.normalized, label); + if (prefixLabelMatchIgnoreCase) { + const prefixLabelMatchStrictCase = matchesStrictPrefix(query.normalized, label); + return { score: prefixLabelMatchStrictCase ? LABEL_PREFIX_SCORE_MATCHCASE : LABEL_PREFIX_SCORE_IGNORECASE, labelMatch: prefixLabelMatchStrictCase || prefixLabelMatchIgnoreCase }; + } + + // Second, score fuzzy const [labelScore, labelPositions] = scoreFuzzy(label, query.normalized, query.normalizedLowercase, fuzzy); if (labelScore) { return { score: labelScore + LABEL_SCORE_THRESHOLD, labelMatch: createMatches(labelPositions) }; @@ -589,7 +600,37 @@ export function compareItemsByFuzzyScore(itemA: T, itemB: T, query: IPrepared } } - // 2.) matches on label are considered higher compared to label+description matches + // 2.) prefer label prefix matches (match case) + if (scoreA === LABEL_PREFIX_SCORE_MATCHCASE || scoreB === LABEL_PREFIX_SCORE_MATCHCASE) { + if (scoreA !== scoreB) { + return scoreA === LABEL_PREFIX_SCORE_MATCHCASE ? -1 : 1; + } + + const labelA = accessor.getItemLabel(itemA) || ''; + const labelB = accessor.getItemLabel(itemB) || ''; + + // prefer shorter names when both match on label prefix + if (labelA.length !== labelB.length) { + return labelA.length - labelB.length; + } + } + + // 3.) prefer label prefix matches (ignore case) + if (scoreA === LABEL_PREFIX_SCORE_IGNORECASE || scoreB === LABEL_PREFIX_SCORE_IGNORECASE) { + if (scoreA !== scoreB) { + return scoreA === LABEL_PREFIX_SCORE_IGNORECASE ? -1 : 1; + } + + const labelA = accessor.getItemLabel(itemA) || ''; + const labelB = accessor.getItemLabel(itemB) || ''; + + // prefer shorter names when both match on label prefix + if (labelA.length !== labelB.length) { + return labelA.length - labelB.length; + } + } + + // 4.) matches on label are considered higher compared to label+description matches if (scoreA > LABEL_SCORE_THRESHOLD || scoreB > LABEL_SCORE_THRESHOLD) { if (scoreA !== scoreB) { return scoreA > scoreB ? -1 : 1; @@ -609,12 +650,12 @@ export function compareItemsByFuzzyScore(itemA: T, itemB: T, query: IPrepared } } - // 3.) compare by score in label+description + // 5.) compare by score in label+description if (scoreA !== scoreB) { return scoreA > scoreB ? -1 : 1; } - // 4.) scores are identical: prefer matches in label over non-label matches + // 6.) scores are identical: prefer matches in label over non-label matches const itemAHasLabelMatches = Array.isArray(itemScoreA.labelMatch) && itemScoreA.labelMatch.length > 0; const itemBHasLabelMatches = Array.isArray(itemScoreB.labelMatch) && itemScoreB.labelMatch.length > 0; if (itemAHasLabelMatches && !itemBHasLabelMatches) { @@ -623,14 +664,14 @@ export function compareItemsByFuzzyScore(itemA: T, itemB: T, query: IPrepared return 1; } - // 5.) scores are identical: prefer more compact matches (label and description) + // 7.) scores are identical: prefer more compact matches (label and description) const itemAMatchDistance = computeLabelAndDescriptionMatchDistance(itemA, itemScoreA, accessor); const itemBMatchDistance = computeLabelAndDescriptionMatchDistance(itemB, itemScoreB, accessor); if (itemAMatchDistance && itemBMatchDistance && itemAMatchDistance !== itemBMatchDistance) { return itemBMatchDistance > itemAMatchDistance ? -1 : 1; } - // 6.) scores are identical: start to use the fallback compare + // 8.) scores are identical: start to use the fallback compare return fallbackCompare(itemA, itemB, query, accessor); } diff --git a/src/vs/base/test/common/fuzzyScorer.test.ts b/src/vs/base/test/common/fuzzyScorer.test.ts index 301cabfc48e..278f2bf5edb 100644 --- a/src/vs/base/test/common/fuzzyScorer.test.ts +++ b/src/vs/base/test/common/fuzzyScorer.test.ts @@ -1010,6 +1010,21 @@ suite('Fuzzy Scorer', () => { assert.equal(res[1], resourceB); }); + test('compareFilesByScore - prefer prefix (bug #103052)', function () { + const resourceA = URI.file('test/smoke/src/main.ts'); + const resourceB = URI.file('src/vs/editor/common/services/semantikTokensProviderStyling.ts'); + + let query = 'smoke main.ts'; + + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + }); + test('prepareQuery', () => { assert.equal(scorer.prepareQuery(' f*a ').normalized, 'fa'); assert.equal(scorer.prepareQuery('model Tester.ts').original, 'model Tester.ts'); From 0a7d9859ed3c64dfcbe86520afd949eafa7451a6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 21 Aug 2020 10:06:14 +0200 Subject: [PATCH 408/736] tackle https://github.com/microsoft/vscode/issues/104606 --- .../workbench/api/common/extHostNotebook.ts | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index a3528ff00c9..1355648af77 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -27,7 +27,6 @@ import * as vscode from 'vscode'; import { Cache } from './cache'; import { ResourceMap } from 'vs/base/common/map'; - interface IObservable { proxy: T; onDidChange: Event; @@ -86,6 +85,8 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell { readonly uri: URI; readonly cellKind: CellKind; + private _document: vscode.TextDocument | undefined; + constructor( private _proxy: MainThreadNotebookShape, readonly notebook: ExtHostNotebookDocument, @@ -112,7 +113,10 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell { } get document(): vscode.TextDocument { - return this._extHostDocument.getDocument(this.uri)!.document; + if (!this._document) { + this._document = this._extHostDocument.getDocument(this.uri)?.document; + } + return this._document!; } get language(): string { @@ -365,6 +369,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo const contentChangeEvents: vscode.NotebookCellsChangeData[] = []; const addedCellDocuments: IExtHostModelAddedData[] = []; + const removedCellDocuments: URI[] = []; splices.reverse().forEach(splice => { const cellDtos = splice[2]; @@ -402,11 +407,16 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo deletedItems, items: newCells }); + + for (let cell of deletedItems) { + removedCellDocuments.push(cell.uri); + } }); - if (addedCellDocuments) { - this._documentsAndEditors.acceptDocumentsAndEditorsDelta({ addedDocuments: addedCellDocuments }); - } + this._documentsAndEditors.acceptDocumentsAndEditorsDelta({ + addedDocuments: addedCellDocuments, + removedDocuments: removedCellDocuments + }); if (!initialization) { this._emitter.emitModelChange({ From f805c84b1b830f9db2efd1be936355ad9f1a83ce Mon Sep 17 00:00:00 2001 From: Sam Poder <39828164+sampoder@users.noreply.github.com> Date: Fri, 21 Aug 2020 16:07:00 +0800 Subject: [PATCH 409/736] Fix spelling mistake in main.js (#104987) Co-authored-by: Benjamin Pasero --- src/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.js b/src/main.js index 9d68c984c66..e4662daa6b4 100644 --- a/src/main.js +++ b/src/main.js @@ -316,7 +316,7 @@ function createDefaultArgvConfigSync(argvConfigPath) { // Default argv content const defaultArgvConfigContent = [ '// This configuration file allows you to pass permanent command line arguments to VS Code.', - '// Only a subset of arguments is currently supported to reduce the likelyhood of breaking', + '// Only a subset of arguments is currently supported to reduce the likelihood of breaking', '// the installation.', '//', '// PLEASE DO NOT CHANGE WITHOUT UNDERSTANDING THE IMPACT', From 7b33afc93292d3f76af0fefb191c7433f42fa3ab Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 21 Aug 2020 10:10:23 +0200 Subject: [PATCH 410/736] :lipstick: --- src/vs/workbench/api/common/extHostNotebook.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 1355648af77..9325f9ae912 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -492,11 +492,11 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo }); } - getCell(cellHandle: number) { + getCell(cellHandle: number): ExtHostCell | undefined { return this.cells.find(cell => cell.handle === cellHandle); } - getCell2(cellUri: UriComponents) { + getCell2(cellUri: UriComponents): ExtHostCell | undefined { return this.cells.find(cell => cell.uri.fragment === cellUri.fragment); } } From 7c314b062371fbf9424290b7d29f5c85f9dbeca2 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 21 Aug 2020 10:44:45 +0200 Subject: [PATCH 411/736] [css] property suggestions not correctly ordered. Fixes #105039 --- 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 ccbdba90a29..331af0883a9 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -10,7 +10,7 @@ "main": "./out/node/cssServerMain", "browser": "./dist/browser/cssServerMain", "dependencies": { - "vscode-css-languageservice": "^4.3.1", + "vscode-css-languageservice": "^4.3.3", "vscode-languageserver": "7.0.0-next.3", "vscode-uri": "^2.1.2" }, diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index 3b9a29250f5..c93e187a941 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -696,10 +696,10 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -vscode-css-languageservice@^4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.3.1.tgz#a78755b28b8a0cbb1681121f0fa372860f34ef6b" - integrity sha512-Vdz2cyoTP2tLWikhFdouK8dAQ3gVhLPxsFkIscM30Quh6rd/YejTeZEYC/W+b0iKumHYebDeo1GUFbf0ptySRw== +vscode-css-languageservice@^4.3.3: + version "4.3.3" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.3.3.tgz#fcb8c7442ae7bb8fbe6ff1d3a514be8248bfb52f" + integrity sha512-b2b+0oHvPmBHygDtOXX3xBvpQCa6eIQSvXnGDNSDmIC1894ZTJ2yX10vjplOO/PvV7mwhyvGPwHyY4X2HGxtKw== dependencies: vscode-languageserver-textdocument "^1.0.1" vscode-languageserver-types "3.16.0-next.2" From 364af775ecb6941f6046135e3aa0dfd857cd8592 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 21 Aug 2020 10:53:46 +0200 Subject: [PATCH 412/736] repl: add line numbers on hover of source elements fixes #105007 --- src/vs/workbench/contrib/debug/browser/replViewer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/replViewer.ts b/src/vs/workbench/contrib/debug/browser/replViewer.ts index ea99871de6d..99b071635d4 100644 --- a/src/vs/workbench/contrib/debug/browser/replViewer.ts +++ b/src/vs/workbench/contrib/debug/browser/replViewer.ts @@ -181,7 +181,7 @@ export class ReplSimpleElementsRenderer implements ITreeRenderer element.sourceData; } From 02cd863122292451376df653c65f213fdcad4e20 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 21 Aug 2020 11:21:48 +0200 Subject: [PATCH 413/736] debug: display launch name when showing dynamic configurations fixes #103471 --- .../workbench/contrib/debug/browser/debugConfigurationManager.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index 0912e0cf7d2..f9c2f462c8b 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -323,6 +323,7 @@ export class ConfigurationManager implements IConfigurationManager { if (launch.workspace && provider) { picks.push(provider.provideDebugConfigurations!(launch.workspace.uri, token.token).then(configurations => configurations.map(config => ({ label: config.name, + description: launch.name, config, buttons: [{ iconClass: 'codicon-gear', From 3723dfa35cea0c589bff318ceea9d3a747c8c7f3 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 21 Aug 2020 11:31:08 +0200 Subject: [PATCH 414/736] Use placeholder unless aria-label given (fixes #104664) --- .../base/parts/quickinput/browser/quickInput.ts | 16 ++++++++-------- .../base/parts/quickinput/common/quickInput.ts | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index 81019137540..d2ca01cf849 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -381,7 +381,7 @@ class QuickPick extends QuickInput implements IQuickPi private static readonly DEFAULT_ARIA_LABEL = localize('quickInputBox.ariaLabel', "Type to narrow down results."); private _value = ''; - private _ariaLabel = QuickPick.DEFAULT_ARIA_LABEL; + private _ariaLabel: string | undefined; private _placeholder: string | undefined; private readonly onDidChangeValueEmitter = this._register(new Emitter()); private readonly onDidAcceptEmitter = this._register(new Emitter()); @@ -435,8 +435,8 @@ class QuickPick extends QuickInput implements IQuickPi filterValue = (value: string) => value; - set ariaLabel(ariaLabel: string) { - this._ariaLabel = ariaLabel || QuickPick.DEFAULT_ARIA_LABEL; + set ariaLabel(ariaLabel: string | undefined) { + this._ariaLabel = ariaLabel; this.update(); } @@ -884,8 +884,11 @@ class QuickPick extends QuickInput implements IQuickPi } if (inputShownJustForScreenReader) { this.ui.inputBox.ariaLabel = ''; - } else if (this.ui.inputBox.ariaLabel !== this.ariaLabel) { - this.ui.inputBox.ariaLabel = this.ariaLabel; + } else { + const ariaLabel = this.ariaLabel || this.placeholder || QuickPick.DEFAULT_ARIA_LABEL; + if (this.ui.inputBox.ariaLabel !== ariaLabel) { + this.ui.inputBox.ariaLabel = ariaLabel; + } } this.ui.list.matchOnDescription = this.matchOnDescription; this.ui.list.matchOnDetail = this.matchOnDetail; @@ -1384,9 +1387,6 @@ export class QuickInputController extends Disposable { ]; input.canSelectMany = !!options.canPickMany; input.placeholder = options.placeHolder; - if (options.placeHolder) { - input.ariaLabel = options.placeHolder; - } input.ignoreFocusOut = !!options.ignoreFocusLost; input.matchOnDescription = !!options.matchOnDescription; input.matchOnDetail = !!options.matchOnDetail; diff --git a/src/vs/base/parts/quickinput/common/quickInput.ts b/src/vs/base/parts/quickinput/common/quickInput.ts index ebad819fffa..de7339e6757 100644 --- a/src/vs/base/parts/quickinput/common/quickInput.ts +++ b/src/vs/base/parts/quickinput/common/quickInput.ts @@ -199,7 +199,7 @@ export interface IQuickPick extends IQuickInput { */ filterValue: (value: string) => string; - ariaLabel: string; + ariaLabel: string | undefined; placeholder: string | undefined; From 3462e29ea04fe2991d6e2a982430b6f44a01f040 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 21 Aug 2020 12:08:41 +0200 Subject: [PATCH 415/736] strict notebook cell api,https://github.com/microsoft/vscode/issues/104606 Let a `vscode.NotebookCell` only be what's spec'd in the API not more. Make things froozen and readonly when possible --- src/vs/vscode.proposed.d.ts | 2 +- .../workbench/api/common/extHostNotebook.ts | 146 +++++++++--------- .../common/extHostNotebookConcatDocument.ts | 6 +- 3 files changed, 78 insertions(+), 76 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 1b98220afd5..7b44dcc0123 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1325,7 +1325,7 @@ declare module 'vscode' { readonly viewType: string; readonly isDirty: boolean; readonly isUntitled: boolean; - readonly cells: NotebookCell[]; + readonly cells: ReadonlyArray; languages: string[]; displayOrder?: GlobPattern[]; metadata: NotebookDocumentMetadata; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 9325f9ae912..b7d99e7e7a8 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -58,7 +58,7 @@ interface INotebookEventEmitter { const addIdToOutput = (output: IRawOutput, id = UUID.generateUuid()): IProcessedOutput => output.outputKind === CellOutputKind.Rich ? ({ ...output, outputId: id }) : output; -export class ExtHostCell extends Disposable implements vscode.NotebookCell { +export class ExtHostCell extends Disposable { public static asModelAddData(notebook: ExtHostNotebookDocument, cell: IMainCellDto): IExtHostModelAddedData { return { @@ -85,49 +85,56 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell { readonly uri: URI; readonly cellKind: CellKind; - private _document: vscode.TextDocument | undefined; + private _cell: vscode.NotebookCell | undefined; constructor( - private _proxy: MainThreadNotebookShape, - readonly notebook: ExtHostNotebookDocument, - private _extHostDocument: ExtHostDocumentsAndEditors, - cell: IMainCellDto, + private readonly _proxy: MainThreadNotebookShape, + private readonly _notebook: ExtHostNotebookDocument, + private readonly _extHostDocument: ExtHostDocumentsAndEditors, + private readonly _cellData: IMainCellDto, ) { super(); - this.handle = cell.handle; - this.uri = URI.revive(cell.uri); - this.cellKind = cell.cellKind; + this.handle = _cellData.handle; + this.uri = URI.revive(_cellData.uri); + this.cellKind = _cellData.cellKind; - this._outputs = cell.outputs; + this._outputs = _cellData.outputs; for (const output of this._outputs) { this._outputMapping.set(output, output.outputId); delete output.outputId; } - const observableMetadata = getObservable(cell.metadata ?? {}); + const observableMetadata = getObservable(_cellData.metadata ?? {}); this._metadata = observableMetadata.proxy; this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => { this._updateMetadata(); })); } - get document(): vscode.TextDocument { - if (!this._document) { - this._document = this._extHostDocument.getDocument(this.uri)?.document; + get cell(): vscode.NotebookCell { + if (!this._cell) { + const that = this; + const document = this._extHostDocument.getDocument(this.uri)!.document; + this._cell = Object.freeze({ + notebook: that._notebook, + uri: that.uri, + cellKind: this._cellData.cellKind, + document, + language: document.languageId, + get outputs() { return that._outputs; }, + set outputs(value) { that._updateOutputs(value); }, + get metadata() { return that._metadata; }, + set metadata(value) { + that.setMetadata(value); + that._updateMetadata(); + }, + }); } - return this._document!; + return this._cell; } - get language(): string { - return this.document.languageId; - } - - get outputs() { - return this._outputs; - } - - set outputs(newOutputs: vscode.CellOutput[]) { + private _updateOutputs(newOutputs: vscode.CellOutput[]) { const rawDiffs = diff(this._outputs || [], newOutputs || [], (a) => { return this._outputMapping.has(a); }); @@ -157,15 +164,6 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell { this._onDidChangeOutputs.fire(transformedDiffs); } - get metadata() { - return this._metadata; - } - - set metadata(newMetadata: vscode.NotebookCellMetadata) { - this.setMetadata(newMetadata); - this._updateMetadata(); - } - setMetadata(newMetadata: vscode.NotebookCellMetadata): void { // Don't apply metadata defaults here, 'undefined' means 'inherit from document metadata' this._metadataChangeListener.dispose(); @@ -177,7 +175,21 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell { } private _updateMetadata(): Promise { - return this._proxy.$updateNotebookCellMetadata(this.notebook.viewType, this.notebook.uri, this.handle, this._metadata); + return this._proxy.$updateNotebookCellMetadata(this._notebook.viewType, this._notebook.uri, this.handle, this._metadata); + } +} + +class RawContentChangeEvent { + + constructor(readonly start: number, readonly deletedCount: number, readonly deletedItems: ExtHostCell[], readonly items: ExtHostCell[]) { } + + static asApiEvent(event: RawContentChangeEvent): vscode.NotebookCellsChangeData { + return Object.freeze({ + start: event.start, + deletedCount: event.deletedCount, + deletedItems: event.deletedItems.map(data => data.cell), + items: event.items.map(data => data.cell) + }); } } @@ -189,8 +201,8 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo private _cellDisposableMapping = new Map(); - get cells() { - return this._cells; + get cells(): ReadonlyArray { + return this._cells.map(cellData => cellData.cell); } private _languages: string[] = []; @@ -367,7 +379,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo return; } - const contentChangeEvents: vscode.NotebookCellsChangeData[] = []; + const contentChangeEvents: RawContentChangeEvent[] = []; const addedCellDocuments: IExtHostModelAddedData[] = []; const removedCellDocuments: URI[] = []; @@ -395,22 +407,16 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo }); for (let j = splice[0]; j < splice[0] + splice[1]; j++) { - this._cellDisposableMapping.get(this.cells[j].handle)?.dispose(); - this._cellDisposableMapping.delete(this.cells[j].handle); + this._cellDisposableMapping.get(this._cells[j].handle)?.dispose(); + this._cellDisposableMapping.delete(this._cells[j].handle); } - const deletedItems = this.cells.splice(splice[0], splice[1], ...newCells); - - contentChangeEvents.push({ - start: splice[0], - deletedCount: splice[1], - deletedItems, - items: newCells - }); - + const deletedItems = this._cells.splice(splice[0], splice[1], ...newCells); for (let cell of deletedItems) { removedCellDocuments.push(cell.uri); } + + contentChangeEvents.push(new RawContentChangeEvent(splice[0], splice[1], deletedItems, newCells)); }); this._documentsAndEditors.acceptDocumentsAndEditorsDelta({ @@ -421,24 +427,24 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo if (!initialization) { this._emitter.emitModelChange({ document: this, - changes: contentChangeEvents + changes: contentChangeEvents.map(RawContentChangeEvent.asApiEvent) }); } } private _moveCell(index: number, newIdx: number): void { - const cells = this.cells.splice(index, 1); - this.cells.splice(newIdx, 0, ...cells); + const cells = this._cells.splice(index, 1); + this._cells.splice(newIdx, 0, ...cells); const changes: vscode.NotebookCellsChangeData[] = [{ start: index, deletedCount: 1, - deletedItems: cells, + deletedItems: cells.map(data => data.cell), items: [] }, { start: newIdx, deletedCount: 0, deletedItems: [], - items: cells + items: cells.map(data => data.cell) }]; this._emitter.emitModelChange({ document: this, @@ -466,15 +472,15 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo } private _changeCellLanguage(index: number, language: string): void { - const cell = this.cells[index]; - const event: vscode.NotebookCellLanguageChangeEvent = { document: this, cell, language }; + const cell = this._cells[index]; + const event: vscode.NotebookCellLanguageChangeEvent = { document: this, cell: cell.cell, language }; this._emitter.emitCellLanguageChange(event); } private _changeCellMetadata(index: number, newMetadata: NotebookCellMetadata): void { - const cell = this.cells[index]; + const cell = this._cells[index]; cell.setMetadata(newMetadata); - const event: vscode.NotebookCellMetadataChangeEvent = { document: this, cell }; + const event: vscode.NotebookCellMetadataChangeEvent = { document: this, cell: cell.cell }; this._emitter.emitCellMetadataChange(event); } @@ -488,16 +494,16 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo await this._proxy.$spliceNotebookCellOutputs(this.viewType, this.uri, cell.handle, outputDtos, Array.from(renderers)); this._emitter.emitCellOutputsChange({ document: this, - cells: [cell] + cells: [cell.cell] }); } getCell(cellHandle: number): ExtHostCell | undefined { - return this.cells.find(cell => cell.handle === cellHandle); + return this._cells.find(cell => cell.handle === cellHandle); } getCell2(cellUri: UriComponents): ExtHostCell | undefined { - return this.cells.find(cell => cell.uri.fragment === cellUri.fragment); + return this._cells.find(cell => cell.uri.fragment === cellUri.fragment); } } @@ -606,7 +612,7 @@ class ExtHostWebviewCommWrapper extends Disposable { export class ExtHostNotebookEditor extends Disposable implements vscode.NotebookEditor { private _viewColumn: vscode.ViewColumn | undefined; - selection?: ExtHostCell = undefined; + selection?: vscode.NotebookCell; private _active: boolean = false; get active(): boolean { @@ -855,7 +861,7 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable { } if (cell) { - return withToken(token => (kernel.executeCell as any)(document, cell, token)); + return withToken(token => (kernel.executeCell as any)(document, cell.cell, token)); } else { return withToken(token => (kernel.executeAllCells as any)(document, token)); } @@ -869,7 +875,7 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable { } if (cell) { - return kernel.cancelCellExecution(document, cell); + return kernel.cancelCellExecution(document, cell.cell); } else { return kernel.cancelAllCellsExecution(document); } @@ -1009,7 +1015,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN outputId: output.outputId, mimeType: output.mimeType, handlerId: id, - transformedOutput: renderer.render(document, cell.outputs[output.index] as vscode.CellDisplayOutput, output.outputId, output.mimeType) + transformedOutput: renderer.render(document, cell.cell.outputs[output.index] as vscode.CellDisplayOutput, output.outputId, output.mimeType) }; }); @@ -1298,7 +1304,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN if (provider.kernel) { if (cell) { - return provider.kernel.cancelCellExecution(document, cell); + return provider.kernel.cancelCellExecution(document, cell.cell); } else { return provider.kernel.cancelAllCellsExecution(document); } @@ -1338,7 +1344,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined; if (cell) { - return withToken(token => (kernelInfo!.kernel.executeCell as any)(document, cell, token)); + return withToken(token => (kernelInfo!.kernel.executeCell as any)(document, cell.cell, token)); } else { return withToken(token => (kernelInfo!.kernel.executeAllCells as any)(document, token)); } @@ -1466,11 +1472,9 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } if (data.selections) { - const cells = editor.editor.document.cells; - if (data.selections.selections.length) { const firstCell = data.selections.selections[0]; - editor.editor.selection = cells.find(cell => cell.handle === firstCell); + editor.editor.selection = editor.editor.document.getCell(firstCell)?.cell; } else { editor.editor.selection = undefined; } @@ -1502,11 +1506,9 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN document ); - const cells = editor.document.cells; - if (selections.length) { const firstCell = selections[0]; - editor.selection = cells.find(cell => cell.handle === firstCell); + editor.selection = editor.document.getCell(firstCell)?.cell; } else { editor.selection = undefined; } diff --git a/src/vs/workbench/api/common/extHostNotebookConcatDocument.ts b/src/vs/workbench/api/common/extHostNotebookConcatDocument.ts index 8ffeda97be3..4702e1c8165 100644 --- a/src/vs/workbench/api/common/extHostNotebookConcatDocument.ts +++ b/src/vs/workbench/api/common/extHostNotebookConcatDocument.ts @@ -6,7 +6,7 @@ import * as types from 'vs/workbench/api/common/extHostTypes'; import * as vscode from 'vscode'; import { Event, Emitter } from 'vs/base/common/event'; -import { ExtHostNotebookController, ExtHostCell } from 'vs/workbench/api/common/extHostNotebook'; +import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; import { DisposableStore } from 'vs/base/common/lifecycle'; @@ -21,7 +21,7 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD private _disposables = new DisposableStore(); private _isClosed = false; - private _cells!: ExtHostCell[]; + private _cells!: vscode.NotebookCell[]; private _cellUris!: ResourceMap; private _cellLengths!: PrefixSumComputer; private _cellLines!: PrefixSumComputer; @@ -78,7 +78,7 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD for (const cell of this._notebook.cells) { if (cell.cellKind === CellKind.Code && (!this._selector || score(this._selector, cell.uri, cell.language, true))) { this._cellUris.set(cell.uri, this._cells.length); - this._cells.push(cell); + this._cells.push(cell); cellLengths.push(cell.document.getText().length + 1); cellLineCounts.push(cell.document.lineCount); } From 6e4fe5fbb688c61bef094f3ec6706229429df852 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 21 Aug 2020 12:39:12 +0200 Subject: [PATCH 416/736] keep old setting as it and add dependent setting --- .../codeEditor/browser/saveParticipants.ts | 18 +++++++++++------- .../files/browser/files.contribution.ts | 17 ++++++++++------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts index 61c5b748882..83a504c0135 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts @@ -239,19 +239,23 @@ class FormatOnSaveParticipant implements ITextFileSaveParticipant { ) }); }); + + const enabled = this.configurationService.getValue('editor.formatOnSave', overrides); + if (!enabled) { + return undefined; + } + const editorOrModel = findEditor(textEditorModel, this.codeEditorService) || textEditorModel; - const config = this.configurationService.getValue('editor.formatOnSave', overrides); - - if (config === true || config === 'file') { - // format the whole file - await this.instantiationService.invokeFunction(formatDocumentWithSelectedProvider, editorOrModel, FormattingMode.Silent, nestedProgress, token); - - } else if (config === 'modifications') { + const mode = this.configurationService.getValue<'file' | 'modifications'>('editor.formatOnSaveMode', overrides); + if (mode === 'modifications') { // format modifications const ranges = await this.instantiationService.invokeFunction(getModifiedRanges, isCodeEditor(editorOrModel) ? editorOrModel.getModel() : editorOrModel); if (ranges) { await this.instantiationService.invokeFunction(formatDocumentRangesWithSelectedProvider, editorOrModel, ranges, FormattingMode.Silent, nestedProgress, token); } + } else { + // format the whole file + await this.instantiationService.invokeFunction(formatDocumentWithSelectedProvider, editorOrModel, FormattingMode.Silent, nestedProgress, token); } } } diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index 0fdfb530004..7be3b5c9b81 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -360,21 +360,24 @@ configurationRegistry.registerConfiguration({ ...editorConfigurationBaseNode, properties: { 'editor.formatOnSave': { + 'type': 'boolean', + 'description': nls.localize('formatOnSave', "Format a file on save. A formatter must be available, the file must not be saved after delay, and the editor must not be shutting down."), + 'scope': ConfigurationScope.LANGUAGE_OVERRIDABLE, + }, + 'editor.formatOnSaveMode': { 'type': 'string', - 'default': 'off', + 'default': 'file', 'enum': [ - 'off', 'file', 'modifications' ], 'enumDescriptions': [ - nls.localize('off', "Disable format on save."), - nls.localize('everything', "Format the whole."), + nls.localize('everything', "Format the whole file."), nls.localize('modification', "Format modifications (requires source control)."), ], - 'description': nls.localize('formatOnSave', "Format a file on save. A formatter must be available, the file must not be saved after delay, and the editor must not be shutting down."), - scope: ConfigurationScope.LANGUAGE_OVERRIDABLE, - } + 'markdownDescription': nls.localize('formatOnSaveMode', "Controls if format on save formats the whole file or only modifications. Only applies when `#editor.formatOnSave#` is `true`."), + 'scope': ConfigurationScope.LANGUAGE_OVERRIDABLE, + }, } }); From fae07df7c35300264ded757c88c0b89140c66a71 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 21 Aug 2020 13:54:31 +0200 Subject: [PATCH 417/736] onTypeRename: wordPattern (for #104823) --- .../client/src/htmlClient.ts | 13 ++++-- src/vs/editor/common/modes.ts | 4 +- src/vs/editor/contrib/rename/onTypeRename.ts | 37 +++++++++++------ .../contrib/rename/test/onTypeRename.test.ts | 41 ++++++++++++------- src/vs/monaco.d.ts | 7 +++- src/vs/vscode.proposed.d.ts | 10 +++-- .../api/browser/mainThreadLanguageFeatures.ts | 17 +++++--- .../workbench/api/common/extHost.protocol.ts | 2 +- .../api/common/extHostLanguageFeatures.ts | 28 +++++++++---- 9 files changed, 105 insertions(+), 54 deletions(-) diff --git a/extensions/html-language-features/client/src/htmlClient.ts b/extensions/html-language-features/client/src/htmlClient.ts index e818d1a2d02..6917b56950b 100644 --- a/extensions/html-language-features/client/src/htmlClient.ts +++ b/extensions/html-language-features/client/src/htmlClient.ts @@ -28,7 +28,7 @@ namespace TagCloseRequest { export const type: RequestType = new RequestType('html/tag'); } namespace OnTypeRenameRequest { - export const type: RequestType = new RequestType('html/onTypeRename'); + export const type: RequestType = new RequestType('html/onTypeRename'); } // experimental: semantic tokens @@ -172,9 +172,14 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua disposable = languages.registerOnTypeRenameProvider(documentSelector, { async provideOnTypeRenameRanges(document, position) { const param = client.code2ProtocolConverter.asTextDocumentPositionParams(document, position); - const response = await client.sendRequest(OnTypeRenameRequest.type, param); - - return response || []; + return client.sendRequest(OnTypeRenameRequest.type, param).then(response => { + if (response) { + return { + ranges: response.map(r => client.protocol2CodeConverter.asRange(r)) + }; + } + return undefined; + }); } }); toDispose.push(disposable); diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index b89285449aa..578e369ec6f 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -813,12 +813,12 @@ export interface DocumentHighlightProvider { */ export interface OnTypeRenameProvider { - stopPattern?: RegExp; + wordPattern?: RegExp; /** * Provide a list of ranges that can be live-renamed together. */ - provideOnTypeRenameRanges(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; + provideOnTypeRenameRanges(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult<{ ranges: IRange[]; wordPattern?: RegExp; }>; } /** diff --git a/src/vs/editor/contrib/rename/onTypeRename.ts b/src/vs/editor/contrib/rename/onTypeRename.ts index ebe4de0bbe3..70cef23cc3a 100644 --- a/src/vs/editor/contrib/rename/onTypeRename.ts +++ b/src/vs/editor/contrib/rename/onTypeRename.ts @@ -29,6 +29,7 @@ import * as strings from 'vs/base/common/strings'; import { registerColor } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { Color } from 'vs/base/common/color'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; export const CONTEXT_ONTYPE_RENAME_INPUT_VISIBLE = new RawContextKey('onTypeRenameInputVisible', false); @@ -58,7 +59,8 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr private _currentRequestModelVersion: number | null; private _currentDecorations: string[]; // The one at index 0 is the reference one - private _stopPattern: RegExp; + private _languageWordPattern: RegExp | null; + private _currentWordPattern: RegExp | null; private _ignoreChangeEvent: boolean; private readonly _localToDispose = this._register(new DisposableStore()); @@ -73,7 +75,8 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr this._visibleContextKey = CONTEXT_ONTYPE_RENAME_INPUT_VISIBLE.bindTo(contextKeyService); this._currentDecorations = []; - this._stopPattern = /\s/; + this._languageWordPattern = null; + this._currentWordPattern = null; this._ignoreChangeEvent = false; this._localToDispose = this._register(new DisposableStore()); @@ -113,6 +116,11 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr return; } + this._languageWordPattern = LanguageConfigurationRegistry.getWordDefinition(model.getLanguageIdentifier().id); + this._localToDispose.add(model.onDidChangeLanguageConfiguration(() => { + this._languageWordPattern = LanguageConfigurationRegistry.getWordDefinition(model.getLanguageIdentifier().id); + })); + const rangeUpdateScheduler = new Delayer(200); const triggerRangeUpdate = () => { this._rangeUpdateTriggerPromise = rangeUpdateScheduler.trigger(() => this.updateRanges()); @@ -160,8 +168,12 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr } const referenceValue = model.getValueInRange(referenceRange); - if (this._stopPattern.test(referenceValue)) { - return this.clearRanges(); + if (this._currentWordPattern) { + const match = referenceValue.match(this._currentWordPattern); + const matchLength = match ? match[0].length : 0; + if (matchLength !== referenceValue.length) { + return this.clearRanges(); + } } let edits: IIdentifiedSingleEditOperation[] = []; @@ -281,9 +293,8 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr if (response?.ranges) { ranges = response.ranges; } - if (response?.stopPattern) { - this._stopPattern = response.stopPattern; - } + + this._currentWordPattern = response?.wordPattern || this._languageWordPattern; let foundReferenceRange = false; for (let i = 0, len = ranges.length; i < len; i++) { @@ -403,7 +414,7 @@ registerEditorCommand(new OnTypeRenameCommand({ export function getOnTypeRenameRanges(model: ITextModel, position: Position, token: CancellationToken): Promise<{ ranges: IRange[], - stopPattern?: RegExp + wordPattern?: RegExp } | undefined | null> { const orderedByScore = OnTypeRenameProviderRegistry.ordered(model); @@ -412,16 +423,16 @@ export function getOnTypeRenameRanges(model: ITextModel, position: Position, tok // (good = none empty array) return first<{ ranges: IRange[], - stopPattern?: RegExp + wordPattern?: RegExp } | undefined>(orderedByScore.map(provider => () => { - return Promise.resolve(provider.provideOnTypeRenameRanges(model, position, token)).then((ranges) => { - if (!ranges) { + return Promise.resolve(provider.provideOnTypeRenameRanges(model, position, token)).then((res) => { + if (!res) { return undefined; } return { - ranges, - stopPattern: provider.stopPattern + ranges: res.ranges, + wordPattern: res.wordPattern || provider.wordPattern }; }, (err) => { onUnexpectedExternalError(err); diff --git a/src/vs/editor/contrib/rename/test/onTypeRename.test.ts b/src/vs/editor/contrib/rename/test/onTypeRename.test.ts index 2d2703df752..0b5fd0b6b56 100644 --- a/src/vs/editor/contrib/rename/test/onTypeRename.test.ts +++ b/src/vs/editor/contrib/rename/test/onTypeRename.test.ts @@ -55,22 +55,21 @@ suite('On type rename', () => { function testCase( name: string, - initialState: { text: string | string[], stopPattern?: RegExp }, + initialState: { text: string | string[], responseWordPattern?: RegExp, providerWordPattern?: RegExp }, operations: (editor: TestEditor) => Promise, expectedEndText: string | string[] ) { test(name, async () => { disposables.add(modes.OnTypeRenameProviderRegistry.register(mockFileSelector, { - stopPattern: initialState.stopPattern || /\s/, - + wordPattern: initialState.providerWordPattern, provideOnTypeRenameRanges(model: ITextModel, pos: IPosition) { const wordAtPos = model.getWordAtPosition(pos); if (wordAtPos) { const matches = model.findMatches(wordAtPos.word, false, false, true, USUAL_WORD_SEPARATORS, false); assert.ok(matches.length > 0); - return matches.map(m => m.range); + return { ranges: matches.map(m => m.range), wordPattern: initialState.responseWordPattern }; } - return []; + return { ranges: [], wordPattern: initialState.responseWordPattern }; } })); @@ -294,12 +293,12 @@ suite('On type rename', () => { }, ''); /** - * Break out with custom stopPattern + * Break out with custom provider wordPattern */ const state3 = { ...state, - stopPattern: /s/ + providerWordPattern: /[a-yA-Y]+/ }; testCase('Breakout with stop pattern - insert', state3, async (editor) => { @@ -311,26 +310,38 @@ suite('On type rename', () => { testCase('Breakout with stop pattern - insert stop char', state3, async (editor) => { const pos = new Position(1, 2); await editor.setPosition(pos); - await editor.trigger('keyboard', Handler.Type, { text: 's' }); - }, ''); + await editor.trigger('keyboard', Handler.Type, { text: 'z' }); + }, ''); testCase('Breakout with stop pattern - paste char', state3, async (editor) => { const pos = new Position(1, 2); await editor.setPosition(pos); - await editor.trigger('keyboard', Handler.Paste, { text: 's' }); - }, ''); + await editor.trigger('keyboard', Handler.Paste, { text: 'z' }); + }, ''); testCase('Breakout with stop pattern - paste string', state3, async (editor) => { const pos = new Position(1, 2); await editor.setPosition(pos); - await editor.trigger('keyboard', Handler.Paste, { text: 'so' }); - }, ''); + await editor.trigger('keyboard', Handler.Paste, { text: 'zo' }); + }, ''); testCase('Breakout with stop pattern - insert at end', state3, async (editor) => { const pos = new Position(1, 5); await editor.setPosition(pos); - await editor.trigger('keyboard', Handler.Type, { text: 's' }); - }, ''); + await editor.trigger('keyboard', Handler.Type, { text: 'z' }); + }, ''); + + const state4 = { + ...state, + providerWordPattern: /[a-yA-Y]+/, + responseWordPattern: /[a-eA-E]+/ + }; + + testCase('Breakout with stop pattern - insert stop char, respos', state4, async (editor) => { + const pos = new Position(1, 2); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + }, ''); /** * Delete diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index d371020b873..54e239c8c65 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -5793,11 +5793,14 @@ declare namespace monaco.languages { * the live-rename feature. */ export interface OnTypeRenameProvider { - stopPattern?: RegExp; + wordPattern?: RegExp; /** * Provide a list of ranges that can be live-renamed together. */ - provideOnTypeRenameRanges(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult; + provideOnTypeRenameRanges(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult<{ + ranges: IRange[]; + wordPattern?: RegExp; + }>; } /** diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 7b44dcc0123..642cd38e7bc 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1064,9 +1064,11 @@ declare module 'vscode' { * @param position The position at which the command was invoked. * @param token A cancellation token. * @return A list of ranges that can be live-renamed togehter. The ranges must have - * identical length and contain identical text content. The ranges cannot overlap. + * identical length and contain identical text content. The ranges cannot overlap. Optional a word pattern + * that overrides the word pattern defined when registering the provider. Live rename stops as soon as the renamed content + * no longer matches the word pattern. */ - provideOnTypeRenameRanges(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + provideOnTypeRenameRanges(document: TextDocument, position: Position, token: CancellationToken): ProviderResult<{ ranges: Range[]; wordPattern?: RegExp; }>; } namespace languages { @@ -1079,10 +1081,10 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider An on type rename provider. - * @param stopPattern Stop on type renaming when input text matches the regular expression. Defaults to `^\s`. + * @param wordPattern Word pattern for this provider. * @return A [disposable](#Disposable) that unregisters this provider when being disposed. */ - export function registerOnTypeRenameProvider(selector: DocumentSelector, provider: OnTypeRenameProvider, stopPattern?: RegExp): Disposable; + export function registerOnTypeRenameProvider(selector: DocumentSelector, provider: OnTypeRenameProvider, wordPattern?: RegExp): Disposable; } //#endregion diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index f7195eaa219..b3133ba44c9 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -263,12 +263,19 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha // --- on type rename - $registerOnTypeRenameProvider(handle: number, selector: IDocumentFilterDto[], stopPattern?: IRegExpDto): void { - const revivedStopPattern = stopPattern ? MainThreadLanguageFeatures._reviveRegExp(stopPattern) : undefined; + $registerOnTypeRenameProvider(handle: number, selector: IDocumentFilterDto[], wordPattern?: IRegExpDto): void { + const revivedWordPattern = wordPattern ? MainThreadLanguageFeatures._reviveRegExp(wordPattern) : undefined; this._registrations.set(handle, modes.OnTypeRenameProviderRegistry.register(selector, { - stopPattern: revivedStopPattern, - provideOnTypeRenameRanges: (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise => { - return this._proxy.$provideOnTypeRenameRanges(handle, model.uri, position, token); + wordPattern: revivedWordPattern, + provideOnTypeRenameRanges: async (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise<{ ranges: IRange[]; wordPattern?: RegExp; } | undefined> => { + const res = await this._proxy.$provideOnTypeRenameRanges(handle, model.uri, position, token); + if (res) { + return { + ranges: res.ranges, + wordPattern: res.wordPattern ? MainThreadLanguageFeatures._reviveRegExp(res.wordPattern) : undefined + }; + } + return undefined; } })); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 0fa76f27f52..17afbf353e8 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1350,7 +1350,7 @@ export interface ExtHostLanguageFeaturesShape { $provideHover(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; $provideEvaluatableExpression(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; $provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; - $provideOnTypeRenameRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; + $provideOnTypeRenameRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<{ ranges: IRange[]; wordPattern?: IRegExpDto; } | undefined>; $provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Promise; $provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise; $releaseCodeActions(handle: number, cacheId: number): void; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index cbe85a2675e..a76867f4531 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -324,14 +324,17 @@ class OnTypeRenameAdapter { private readonly _provider: vscode.OnTypeRenameProvider ) { } - provideOnTypeRenameRanges(resource: URI, position: IPosition, token: CancellationToken): Promise { + provideOnTypeRenameRanges(resource: URI, position: IPosition, token: CancellationToken): Promise<{ ranges: IRange[]; wordPattern?: RegExp; } | undefined> { const doc = this._documents.getDocument(resource); const pos = typeConvert.Position.to(position); return asPromise(() => this._provider.provideOnTypeRenameRanges(doc, pos, token)).then(value => { - if (Array.isArray(value)) { - return coalesce(value.map(typeConvert.Range.from)); + if (value && Array.isArray(value.ranges)) { + return { + ranges: coalesce(value.ranges.map(typeConvert.Range.from)), + wordPattern: value.wordPattern + }; } return undefined; }); @@ -1549,15 +1552,24 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF // --- on type rename - registerOnTypeRenameProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.OnTypeRenameProvider, stopPattern?: RegExp): vscode.Disposable { + registerOnTypeRenameProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.OnTypeRenameProvider, wordPattern?: RegExp): vscode.Disposable { const handle = this._addNewAdapter(new OnTypeRenameAdapter(this._documents, provider), extension); - const serializedStopPattern = stopPattern ? ExtHostLanguageFeatures._serializeRegExp(stopPattern) : undefined; - this._proxy.$registerOnTypeRenameProvider(handle, this._transformDocumentSelector(selector), serializedStopPattern); + const serializedWordPattern = wordPattern ? ExtHostLanguageFeatures._serializeRegExp(wordPattern) : undefined; + this._proxy.$registerOnTypeRenameProvider(handle, this._transformDocumentSelector(selector), serializedWordPattern); return this._createDisposable(handle); } - $provideOnTypeRenameRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise { - return this._withAdapter(handle, OnTypeRenameAdapter, adapter => adapter.provideOnTypeRenameRanges(URI.revive(resource), position, token), undefined); + $provideOnTypeRenameRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<{ ranges: IRange[]; wordPattern?: extHostProtocol.IRegExpDto; } | undefined> { + return this._withAdapter(handle, OnTypeRenameAdapter, async adapter => { + const res = await adapter.provideOnTypeRenameRanges(URI.revive(resource), position, token); + if (res) { + return { + ranges: res.ranges, + wordPattern: res.wordPattern ? ExtHostLanguageFeatures._serializeRegExp(res.wordPattern) : undefined + }; + } + return undefined; + }, undefined); } // --- references From e2efa92868849f2b02059fc7430720412fb13ee1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 21 Aug 2020 13:58:15 +0200 Subject: [PATCH 418/736] chore - TextEditor.viewColumn is readonly --- src/vs/vscode.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 8a6c2307d02..229d11ad3d1 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -1112,7 +1112,7 @@ declare module 'vscode' { * isn't one of the main editors, e.g. an embedded editor, or when the editor * column is larger than three. */ - viewColumn?: ViewColumn; + readonly viewColumn?: ViewColumn; /** * Perform an edit on the document associated with this text editor. From 25ad7485b3aa2dea43ab945b4f6af94cf0537b09 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 21 Aug 2020 14:58:43 +0200 Subject: [PATCH 419/736] chore - remove unused, bad-layered util-function --- src/vs/base/common/strings.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index 5003acabe09..ec727797b87 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -855,10 +855,6 @@ export function stripUTF8BOM(str: string): string { return startsWithUTF8BOM(str) ? str.substr(1) : str; } -export function safeBtoa(str: string): string { - return btoa(encodeURIComponent(str)); // we use encodeURIComponent because btoa fails for non Latin 1 values -} - /** * @deprecated ES6 */ From 06730e3e29c3d8a798a4402f116cbff47a1a7bc1 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 21 Aug 2020 15:05:15 +0200 Subject: [PATCH 420/736] Azure DevOps pipeline - generate unique test result names (#105148) * Set suite name based on environment variable --- extensions/emmet/src/test/index.ts | 14 ++++++++++++-- extensions/git/src/test/index.ts | 14 ++++++++++++-- .../markdown-language-features/src/test/index.ts | 14 ++++++++++++-- .../src/singlefolder-tests/index.ts | 14 ++++++++++++-- .../vscode-api-tests/src/workspace-tests/index.ts | 14 ++++++++++++-- extensions/vscode-notebook-tests/src/index.ts | 14 ++++++++++++-- test/integration/browser/src/index.ts | 9 ++++++--- 7 files changed, 78 insertions(+), 15 deletions(-) diff --git a/extensions/emmet/src/test/index.ts b/extensions/emmet/src/test/index.ts index dc77f1beb82..3aeda9dfa42 100644 --- a/extensions/emmet/src/test/index.ts +++ b/extensions/emmet/src/test/index.ts @@ -6,14 +6,24 @@ const path = require('path'); const testRunner = require('vscode/lib/testrunner'); -const suite = 'Integration Emmet Tests'; - const options: any = { ui: 'tdd', useColors: (!process.env.BUILD_ARTIFACTSTAGINGDIRECTORY && process.platform !== 'win32'), timeout: 60000 }; +// These integration tests is being run in multiple environments (electron, web, remote) +// so we need to set the suite name based on the environment as the suite name is used +// for the test results file name +let suite = ''; +if (process.env.VSCODE_BROWSER) { + suite = `${process.env.VSCODE_BROWSER} Browser Integration Emmet Tests`; +} else if (process.env.REMOTE_VSCODE) { + suite = 'Remote Integration Emmet Tests'; +} else { + suite = 'Integration Emmet Tests'; +} + if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) { options.reporter = 'mocha-multi-reporters'; options.reporterOptions = { diff --git a/extensions/git/src/test/index.ts b/extensions/git/src/test/index.ts index a3588b2238c..8773f772e62 100644 --- a/extensions/git/src/test/index.ts +++ b/extensions/git/src/test/index.ts @@ -6,14 +6,24 @@ const path = require('path'); const testRunner = require('vscode/lib/testrunner'); -const suite = 'Integration Git Tests'; - const options: any = { ui: 'tdd', useColors: (!process.env.BUILD_ARTIFACTSTAGINGDIRECTORY && process.platform !== 'win32'), timeout: 60000 }; +// These integration tests is being run in multiple environments (electron, web, remote) +// so we need to set the suite name based on the environment as the suite name is used +// for the test results file name +let suite = ''; +if (process.env.VSCODE_BROWSER) { + suite = `${process.env.VSCODE_BROWSER} Browser Integration Git Tests`; +} else if (process.env.REMOTE_VSCODE) { + suite = 'Remote Integration Git Tests'; +} else { + suite = 'Integration Git Tests'; +} + if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) { options.reporter = 'mocha-multi-reporters'; options.reporterOptions = { diff --git a/extensions/markdown-language-features/src/test/index.ts b/extensions/markdown-language-features/src/test/index.ts index a6d471ee2c1..0eb9bc92487 100644 --- a/extensions/markdown-language-features/src/test/index.ts +++ b/extensions/markdown-language-features/src/test/index.ts @@ -6,14 +6,24 @@ const path = require('path'); const testRunner = require('vscode/lib/testrunner'); -const suite = 'Integration Markdown Tests'; - const options: any = { ui: 'tdd', useColors: (!process.env.BUILD_ARTIFACTSTAGINGDIRECTORY && process.platform !== 'win32'), timeout: 60000 }; +// These integration tests is being run in multiple environments (electron, web, remote) +// so we need to set the suite name based on the environment as the suite name is used +// for the test results file name +let suite = ''; +if (process.env.VSCODE_BROWSER) { + suite = `${process.env.VSCODE_BROWSER} Browser Integration Markdown Tests`; +} else if (process.env.REMOTE_VSCODE) { + suite = 'Remote Integration Markdown Tests'; +} else { + suite = 'Integration Markdown Tests'; +} + if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) { options.reporter = 'mocha-multi-reporters'; options.reporterOptions = { diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/index.ts b/extensions/vscode-api-tests/src/singlefolder-tests/index.ts index 3bd54c52ef6..ea50f38b1b9 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/index.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/index.ts @@ -6,14 +6,24 @@ const path = require('path'); const testRunner = require('vscode/lib/testrunner'); -const suite = 'Integration Single Folder Tests'; - const options: any = { ui: 'tdd', useColors: (!process.env.BUILD_ARTIFACTSTAGINGDIRECTORY && process.platform !== 'win32'), timeout: 60000 }; +// These integration tests is being run in multiple environments (electron, web, remote) +// so we need to set the suite name based on the environment as the suite name is used +// for the test results file name +let suite = ''; +if (process.env.VSCODE_BROWSER) { + suite = `${process.env.VSCODE_BROWSER} Browser Integration Single Folder Tests`; +} else if (process.env.REMOTE_VSCODE) { + suite = 'Remote Integration Single Folder Tests'; +} else { + suite = 'Integration Single Folder Tests'; +} + if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) { options.reporter = 'mocha-multi-reporters'; options.reporterOptions = { diff --git a/extensions/vscode-api-tests/src/workspace-tests/index.ts b/extensions/vscode-api-tests/src/workspace-tests/index.ts index 7f032ab0b5e..9486d8ed3e5 100644 --- a/extensions/vscode-api-tests/src/workspace-tests/index.ts +++ b/extensions/vscode-api-tests/src/workspace-tests/index.ts @@ -6,14 +6,24 @@ const path = require('path'); const testRunner = require('vscode/lib/testrunner'); -const suite = 'Integration Workspace Tests'; - const options: any = { ui: 'tdd', useColors: (!process.env.BUILD_ARTIFACTSTAGINGDIRECTORY && process.platform !== 'win32'), timeout: 60000 }; +// These integration tests is being run in multiple environments (electron, web, remote) +// so we need to set the suite name based on the environment as the suite name is used +// for the test results file name +let suite = ''; +if (process.env.VSCODE_BROWSER) { + suite = `${process.env.VSCODE_BROWSER} Browser Integration Workspace Tests`; +} else if (process.env.REMOTE_VSCODE) { + suite = 'Remote Integration Workspace Tests'; +} else { + suite = 'Integration Workspace Tests'; +} + if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) { options.reporter = 'mocha-multi-reporters'; options.reporterOptions = { diff --git a/extensions/vscode-notebook-tests/src/index.ts b/extensions/vscode-notebook-tests/src/index.ts index 2da40bed655..293c02db743 100644 --- a/extensions/vscode-notebook-tests/src/index.ts +++ b/extensions/vscode-notebook-tests/src/index.ts @@ -6,14 +6,24 @@ const path = require('path'); const testRunner = require('vscode/lib/testrunner'); -const suite = 'Integration Notebook Tests'; - const options: any = { ui: 'tdd', useColors: (!process.env.BUILD_ARTIFACTSTAGINGDIRECTORY && process.platform !== 'win32'), timeout: 60000 }; +// These integration tests is being run in multiple environments (electron, web, remote) +// so we need to set the suite name based on the environment as the suite name is used +// for the test results file name +let suite = ''; +if (process.env.VSCODE_BROWSER) { + suite = `${process.env.VSCODE_BROWSER} Browser Integration Notebook Tests`; +} else if (process.env.REMOTE_VSCODE) { + suite = 'Remote Integration Notebook Tests'; +} else { + suite = 'Integration Notebook Tests'; +} + if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) { options.reporter = 'mocha-multi-reporters'; options.reporterOptions = { diff --git a/test/integration/browser/src/index.ts b/test/integration/browser/src/index.ts index 91bdd3a8322..5ce67dd0548 100644 --- a/test/integration/browser/src/index.ts +++ b/test/integration/browser/src/index.ts @@ -29,7 +29,9 @@ if (optimist.argv.help) { const width = 1200; const height = 800; -async function runTestsInBrowser(browserType: 'chromium' | 'firefox' | 'webkit', endpoint: url.UrlWithStringQuery, server: cp.ChildProcess): Promise { +type BrowserType = 'chromium' | 'firefox' | 'webkit'; + +async function runTestsInBrowser(browserType: BrowserType, endpoint: url.UrlWithStringQuery, server: cp.ChildProcess): Promise { const args = process.platform === 'linux' && browserType === 'chromium' ? ['--no-sandbox'] : undefined; // disable sandbox to run chrome on certain Linux distros const browser = await playwright[browserType].launch({ headless: !Boolean(optimist.argv.debug), args }); const context = await browser.newContext(); @@ -78,7 +80,7 @@ function pkill(pid: number): Promise { }); } -async function launchServer(): Promise<{ endpoint: url.UrlWithStringQuery, server: cp.ChildProcess }> { +async function launchServer(browserType: BrowserType): Promise<{ endpoint: url.UrlWithStringQuery, server: cp.ChildProcess }> { // Ensure a tmp user-data-dir is used for the tests const tmpDir = tmp.dirSync({ prefix: 't' }); @@ -89,6 +91,7 @@ async function launchServer(): Promise<{ endpoint: url.UrlWithStringQuery, serve const env = { VSCODE_AGENT_FOLDER: userDataDir, + VSCODE_BROWSER: browserType, ...process.env }; @@ -130,7 +133,7 @@ async function launchServer(): Promise<{ endpoint: url.UrlWithStringQuery, serve }); } -launchServer().then(async ({ endpoint, server }) => { +launchServer(optimist.argv.browser).then(async ({ endpoint, server }) => { return runTestsInBrowser(optimist.argv.browser, endpoint, server); }, error => { console.error(error); From 9ba9794e223bb1940abee60f0a76978110bbd945 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 21 Aug 2020 16:20:06 +0200 Subject: [PATCH 421/736] nb - viewColumn is readonly --- 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 642cd38e7bc..f41af9aeb90 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1371,7 +1371,7 @@ declare module 'vscode' { /** * The column in which this editor shows. */ - viewColumn?: ViewColumn; + readonly viewColumn?: ViewColumn; /** * Whether the panel is active (focused by the user). From 90d2cac44d92290ce4d3a2008321359fdcec102e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 21 Aug 2020 16:20:43 +0200 Subject: [PATCH 422/736] nb - test that cell removal cleans-up documents --- .../test/browser/api/extHostNotebook.test.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts index d088232e237..cac9483fc13 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts @@ -225,6 +225,24 @@ suite('NotebookCell#Document', function () { } }); + test('cell document goes when cell is removed', async function () { + + assert.equal(notebook.cells.length, 2); + const [cell1, cell2] = notebook.cells; + + extHostNotebooks.$acceptModelChanged(notebook.uri, { + kind: NotebookCellsChangeType.ModelChange, + versionId: 2, + changes: [[0, 1, []]] + }); + + assert.equal(notebook.cells.length, 1); + assert.equal(cell1.document.isClosed, true); // ref still alive! + assert.equal(cell2.document.isClosed, false); + + assert.throws(() => extHostDocuments.getDocument(cell1.uri)); + }); + test('cell document knows notebook', function () { for (let cells of notebook.cells) { assert.equal(cells.document.notebook === notebook, true); From 325e07f06c463779aa8537962f8ccf1fe9e50224 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 21 Aug 2020 08:33:26 -0700 Subject: [PATCH 423/736] don't create promise when it can't be resolved --- .../services/experiment/electron-browser/experimentService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/experiment/electron-browser/experimentService.ts b/src/vs/workbench/services/experiment/electron-browser/experimentService.ts index 8a5016a8f51..86e4db99d35 100644 --- a/src/vs/workbench/services/experiment/electron-browser/experimentService.ts +++ b/src/vs/workbench/services/experiment/electron-browser/experimentService.ts @@ -172,7 +172,7 @@ export class ExperimentService implements ITASExperimentService { @IConfigurationService private configurationService: IConfigurationService, ) { - if (this.productService.tasConfig && this.experimentsEnabled) { + if (this.productService.tasConfig && this.experimentsEnabled && this.telemetryService.isOptedIn) { this.tasClient = this.setupTASClient(); } } From 83bea8f0e45a17f72b35d97b052e11fe09702cb7 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 21 Aug 2020 09:16:08 -0700 Subject: [PATCH 424/736] tests: probably fix occasional test error on NamedPipeDebugAdapter Refs: https://github.com/microsoft/vscode/issues/105136 --- .../contrib/debug/test/node/streamDebugAdapter.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/test/node/streamDebugAdapter.test.ts b/src/vs/workbench/contrib/debug/test/node/streamDebugAdapter.test.ts index 52977226fa5..470565e2d7a 100644 --- a/src/vs/workbench/contrib/debug/test/node/streamDebugAdapter.test.ts +++ b/src/vs/workbench/contrib/debug/test/node/streamDebugAdapter.test.ts @@ -49,7 +49,7 @@ function serverConnection(socket: net.Socket) { suite('Debug - StreamDebugAdapter', () => { const port = rndPort(); - const pipeName = crypto.randomBytes(10).toString('utf8'); + const pipeName = crypto.randomBytes(10).toString('hex'); const pipePath = platform.isWindows ? join('\\\\.\\pipe\\', pipeName) : join(tmpdir(), pipeName); const testCases: { testName: string, debugAdapter: StreamDebugAdapter, connectionDetail: string | number }[] = [ From b844ec09cda6ce54f2c4e4b071c1fa72b818db35 Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 21 Aug 2020 10:04:00 -0700 Subject: [PATCH 425/736] abstract cell renderer --- .../notebook/browser/diff/cellComponents.ts | 492 ++++++++++++++++++ .../browser/diff/celllDiffViewModel.ts | 19 +- .../contrib/notebook/browser/diff/common.ts | 11 + .../browser/diff/notebookTextDiffEditor.ts | 23 +- .../browser/diff/notebookTextDiffList.ts | 440 +--------------- .../browser/viewModel/eventDispatcher.ts | 21 + 6 files changed, 562 insertions(+), 444 deletions(-) create mode 100644 src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts new file mode 100644 index 00000000000..9445d33056a --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -0,0 +1,492 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as DOM from 'vs/base/browser/dom'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { CellDiffViewModel } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; +import { CellDiffRenderTemplate, CellDiffViewModelLayoutChangeEvent, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/common'; +import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; +import { renderCodicons } from 'vs/base/common/codicons'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { format } from 'vs/base/common/jsonFormatter'; +import { applyEdits } from 'vs/base/common/jsonEdit'; + + +const fixedDiffEditorOptions: IEditorOptions = { + padding: { + top: 12, + bottom: 12 + }, + scrollBeyondLastLine: false, + scrollbar: { + verticalScrollbarSize: 14, + horizontal: 'auto', + useShadows: true, + verticalHasArrows: false, + horizontalHasArrows: false, + alwaysConsumeMouseWheel: false + }, + renderLineHighlightOnlyWhenFocus: true, + overviewRulerLanes: 0, + selectOnLineNumbers: false, + wordWrap: 'off', + lineNumbers: 'off', + lineDecorationsWidth: 0, + glyphMargin: true, + fixedOverflowWidgets: true, + minimap: { enabled: false }, + renderValidationDecorations: 'on' +}; + +const fixedEditorOptions: IEditorOptions = { + padding: { + top: 12, + bottom: 12 + }, + scrollBeyondLastLine: false, + scrollbar: { + verticalScrollbarSize: 14, + horizontal: 'auto', + useShadows: true, + verticalHasArrows: false, + horizontalHasArrows: false, + alwaysConsumeMouseWheel: false + }, + renderLineHighlightOnlyWhenFocus: true, + overviewRulerLanes: 0, + selectOnLineNumbers: false, + wordWrap: 'off', + lineNumbers: 'off', + lineDecorationsWidth: 0, + glyphMargin: false, + fixedOverflowWidgets: true, + minimap: { enabled: false }, + renderValidationDecorations: 'on' +}; + +enum MetadataFoldingState { + Expanded, + Collapsed +} + +abstract class AbstractCellRenderer extends Disposable { + constructor( + readonly notebookEditor: INotebookTextDiffEditor, + readonly cell: CellDiffViewModel, + ) { + super(); + + this._register(cell.onDidLayoutChange(e => this.onDidLayoutChange(e))); + } + + abstract onDidLayoutChange(event: CellDiffViewModelLayoutChangeEvent): void; +} + +export class UnchangedCell extends AbstractCellRenderer { + private _editor!: CodeEditorWidget; + private _metadataEditor?: CodeEditorWidget; + private _layoutInfo!: { + editorHeight: number; + editorMargin: number; + metadataStatusHeight: number; + metadataHeight: number; + }; + + private _foldingState: MetadataFoldingState; + private _metadataHeaderContainer!: HTMLElement; + private _metadataInfoContainer!: HTMLElement; + private _metadataEditorContainer?: HTMLElement; + private _foldingIndicator!: HTMLElement; + private _metadataEditorDisposeStore = new DisposableStore(); + + constructor( + readonly notebookEditor: INotebookTextDiffEditor, + readonly cell: CellDiffViewModel, + readonly templateData: CellDiffRenderTemplate, + @IModeService private readonly modeService: IModeService, + @IModelService protected modelService: IModelService, + @IInstantiationService protected readonly instantiationService: IInstantiationService, + ) { + super(notebookEditor, cell); + + // init + this._layoutInfo = { + editorHeight: 0, + editorMargin: 32, + metadataHeight: 0, + metadataStatusHeight: 24 + }; + + this._foldingState = MetadataFoldingState.Collapsed; + + const diffEditorContainer = DOM.$('.cell-diff-editor-container'); + DOM.append(templateData.container, diffEditorContainer); + + const originalCell = cell.original!; + const lineCount = originalCell.textBuffer.getLineCount(); + const lineHeight = notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; + const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; + const editorContainer = DOM.append(diffEditorContainer, DOM.$('.editor-container')); + this._metadataHeaderContainer = DOM.append(diffEditorContainer, DOM.$('.metadata-container')); + this._metadataInfoContainer = DOM.append(diffEditorContainer, DOM.$('.metadata-info-container')); + this.buildMetadata(); + + this._editor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, { + ...fixedEditorOptions, + dimension: { + width: notebookEditor.getLayoutInfo().width - 20, + height: editorHeight + } + }, {}); + + this._register(this._editor.onDidContentSizeChange((e) => { + if (e.contentHeightChanged) { + this._layoutInfo.editorHeight = e.contentHeight; + this.layout({ editorHeight: true }); + } + })); + + originalCell.resolveTextModelRef().then(ref => { + this._register(ref); + + const textModel = ref.object.textEditorModel; + this._editor.setModel(textModel); + + this._layoutInfo.editorHeight = this._editor.getContentHeight(); + this.layout({ editorHeight: true }); + }); + } + + onDidLayoutChange(event: CellDiffViewModelLayoutChangeEvent): void { + if (event.outerWidth !== undefined) { + this.layout({ outerWidth: true }); + } + } + + layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean }) { + if (state.editorHeight || state.outerWidth) { + this._editor.layout({ + width: this.notebookEditor.getLayoutInfo().width - 20, + height: this._layoutInfo.editorHeight + }); + } + + if (state.metadataEditor || state.outerWidth) { + this._metadataEditor?.layout({ + width: this.notebookEditor.getLayoutInfo().width - 20, + height: this._layoutInfo.metadataHeight + }); + } + this.notebookEditor.layoutNotebookCell(this.cell, + this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight); + } + + private _updateFoldingIcon() { + if (this._foldingState === MetadataFoldingState.Collapsed) { + this._foldingIndicator.innerHTML = renderCodicons('$(chevron-right)'); + } else { + this._foldingIndicator.innerHTML = renderCodicons('$(chevron-down)'); + } + } + + buildMetadata() { + this._foldingIndicator = DOM.append(this._metadataHeaderContainer, DOM.$('.metadata-folding-indicator')); + this._updateFoldingIcon(); + const metadataStatus = DOM.append(this._metadataHeaderContainer, DOM.$('div.metadata-status')); + const metadataStatusSpan = DOM.append(metadataStatus, DOM.$('span')); + metadataStatusSpan.textContent = 'Metadata unchanged'; + + this._register(this.notebookEditor.onMouseUp(e => { + if (!e.event.target) { + return; + } + + const target = e.event.target as HTMLElement; + + if (DOM.hasClass(target, 'codicon-chevron-down') || DOM.hasClass(target, 'codicon-chevron-right')) { + const parent = target.parentElement as HTMLElement; + + if (parent && !DOM.hasClass(parent, 'metadata-folding-indicator')) { + return; + } + + // folding icon + + const cellViewModel = e.target; + + if (cellViewModel === this.cell) { + this._foldingState = this._foldingState === MetadataFoldingState.Expanded ? MetadataFoldingState.Collapsed : MetadataFoldingState.Expanded; + this.updateMetadataRendering(); + } + } + + return; + })); + + this.updateMetadataRendering(); + } + + updateMetadataRendering() { + if (this._foldingState === MetadataFoldingState.Expanded) { + // we should expand the metadata editor + this._metadataInfoContainer.style.display = 'block'; + + if (!this._metadataEditorContainer || !this._metadataEditor) { + // create editor + this._metadataEditorContainer = DOM.append(this._metadataInfoContainer, DOM.$('.metadata-editor-container')); + + this._metadataEditor = this.instantiationService.createInstance(CodeEditorWidget, this._metadataEditorContainer, { + ...fixedEditorOptions, + dimension: { + width: this.notebookEditor.getLayoutInfo().width - 20, + height: 0 + } + }, {}); + + const mode = this.modeService.create('json'); + const content = JSON.stringify(this.cell.original!.metadata); + const edits = format(content, undefined, {}); + const metadataSource = applyEdits(content, edits); + const metadataModel = this.modelService.createModel(metadataSource, mode, undefined, true); + this._metadataEditor.setModel(metadataModel); + + this._layoutInfo.metadataHeight = this._metadataEditor.getContentHeight(); + console.log(this._layoutInfo.metadataHeight); + this.layout({ metadataEditor: true }); + + this._register(this._metadataEditor.onDidContentSizeChange((e) => { + if (e.contentHeightChanged && this._foldingState === MetadataFoldingState.Expanded) { + this._layoutInfo.metadataHeight = e.contentHeight; + this.layout({ metadataEditor: true }); + } + })); + } else { + this._layoutInfo.metadataHeight = this._metadataEditor.getContentHeight(); + this.layout({ metadataEditor: true }); + } + } else { + // we should collapse the metadata editor + this._metadataInfoContainer.style.display = 'none'; + this._metadataEditorDisposeStore.clear(); + this._layoutInfo.metadataHeight = 0; + this.layout({}); + } + + this._updateFoldingIcon(); + + } +} + +export class DeletedCell extends AbstractCellRenderer { + private _editor!: CodeEditorWidget; + constructor( + readonly notebookEditor: INotebookTextDiffEditor, + readonly cell: CellDiffViewModel, + readonly templateData: CellDiffRenderTemplate, + @IInstantiationService protected readonly instantiationService: IInstantiationService, + ) { + super(notebookEditor, cell); + const diffEditorContainer = DOM.$('.cell-diff-editor-container'); + DOM.append(templateData.container, diffEditorContainer); + DOM.addClass(diffEditorContainer, 'delete'); + + const originalCell = cell.original!; + const lineCount = originalCell.textBuffer.getLineCount(); + const lineHeight = notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; + const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; + const editorContainer = DOM.append(diffEditorContainer, DOM.$('.editor-container')); + const diagonalFill = DOM.append(diffEditorContainer, DOM.$('.diagonal-fill')); + + this._editor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, { + ...fixedEditorOptions, + dimension: { + width: (notebookEditor.getLayoutInfo().width - 20) / 2 - 18, + height: editorHeight + } + }, {}); + + diagonalFill.style.height = `${editorHeight}px`; + + this._register(this._editor.onDidContentSizeChange((e) => { + if (e.contentHeightChanged) { + this._editor.layout({ + width: (notebookEditor.getLayoutInfo().width - 20) / 2 - 18, + height: e.contentHeight + }); + diagonalFill.style.height = `${e.contentHeight}px`; + this.notebookEditor.layoutNotebookCell(this.cell, e.contentHeight + 32); + } + })); + + originalCell.resolveTextModelRef().then(ref => { + this._register(ref); + + const textModel = ref.object.textEditorModel; + this._editor.setModel(textModel); + + this.notebookEditor.layoutNotebookCell(this.cell, this._editor.getContentHeight() + 32); + diagonalFill.style.height = `${this._editor.getContentHeight()}px`; + }); + } + + onDidLayoutChange(e: CellDiffViewModelLayoutChangeEvent) { + if (e.outerWidth !== undefined) { + this._editor.layout({ + width: (this.notebookEditor.getLayoutInfo().width - 20) / 2 - 18, + height: this._editor.getContentHeight() + }); + } + } +} + +export class InsertCell extends AbstractCellRenderer { + private _editor!: CodeEditorWidget; + constructor( + readonly notebookEditor: INotebookTextDiffEditor, + readonly cell: CellDiffViewModel, + readonly templateData: CellDiffRenderTemplate, + @IInstantiationService protected readonly instantiationService: IInstantiationService, + ) { + super(notebookEditor, cell); + const diffEditorContainer = DOM.$('.cell-diff-editor-container'); + DOM.append(templateData.container, diffEditorContainer); + DOM.addClass(diffEditorContainer, 'insert'); + + const modifiedCell = cell.modified!; + const lineCount = modifiedCell.textBuffer.getLineCount(); + const lineHeight = notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; + const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; + const diagonalFill = DOM.append(diffEditorContainer, DOM.$('.diagonal-fill')); + const editorContainer = DOM.append(diffEditorContainer, DOM.$('.editor-container')); + + this._editor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, { + ...fixedEditorOptions, + dimension: { + width: (notebookEditor.getLayoutInfo().width - 20) / 2 - 18, + height: editorHeight + } + }, {}); + + diagonalFill.style.height = `${editorHeight}px`; + + this._register(this._editor.onDidContentSizeChange((e) => { + if (e.contentHeightChanged) { + this._editor.layout({ + width: (notebookEditor.getLayoutInfo().width - 20) / 2 - 18, + height: e.contentHeight + }); + diagonalFill.style.height = `${e.contentHeight}px`; + this.notebookEditor.layoutNotebookCell(this.cell, e.contentHeight + 32); + } + })); + + this._register(this.cell.onDidLayoutChange(e => { + if (e.outerWidth !== undefined) { + this._editor.layout({ + width: (notebookEditor.getLayoutInfo().width - 20) / 2 - 18, + height: this._editor.getContentHeight() + }); + } + })); + + modifiedCell.resolveTextModelRef().then(ref => { + this._register(ref); + + const textModel = ref.object.textEditorModel; + this._editor.setModel(textModel); + + this.notebookEditor.layoutNotebookCell(this.cell, this._editor.getContentHeight() + 32); + diagonalFill.style.height = `${this._editor.getContentHeight()}px`; + }); + } + + onDidLayoutChange(e: CellDiffViewModelLayoutChangeEvent) { + if (e.outerWidth !== undefined) { + this._editor.layout({ + width: (this.notebookEditor.getLayoutInfo().width - 20) / 2 - 18, + height: this._editor.getContentHeight() + }); + } + } +} + +export class ModifiedCell extends AbstractCellRenderer { + private _editor!: DiffEditorWidget; + private _editorContainer!: HTMLElement; + constructor( + readonly notebookEditor: INotebookTextDiffEditor, + readonly cell: CellDiffViewModel, + readonly templateData: CellDiffRenderTemplate, + @IInstantiationService protected readonly instantiationService: IInstantiationService, + ) { + super(notebookEditor, cell); + const diffEditorContainer = DOM.$('.cell-diff-editor-container'); + DOM.append(templateData.container, diffEditorContainer); + + const modifiedCell = cell.modified!; + const lineCount = modifiedCell.textBuffer.getLineCount(); + const lineHeight = notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; + const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; + this._editorContainer = DOM.append(diffEditorContainer, DOM.$('.editor-container')); + + this._editor = this.instantiationService.createInstance(DiffEditorWidget, this._editorContainer, { + ...fixedDiffEditorOptions + }); + + this._editor.layout({ + width: notebookEditor.getLayoutInfo().width - 20, + height: editorHeight + }); + + this._editorContainer.style.height = `${editorHeight}px`; + + this._register(this._editor.onDidContentSizeChange((e) => { + if (e.contentHeightChanged) { + this._editorContainer.style.height = `${e.contentHeight}px`; + this.notebookEditor.layoutNotebookCell(this.cell, e.contentHeight + 32); + this._editor.layout(); + } + })); + + this.initialize(); + } + + onDidLayoutChange(e: CellDiffViewModelLayoutChangeEvent) { + if (e.outerWidth !== undefined) { + this._editor.layout({ + width: this.notebookEditor.getLayoutInfo().width - 20, + height: this._editor.getContentHeight() + }); + } + } + + async initialize() { + const originalCell = this.cell.original!; + const modifiedCell = this.cell.modified!; + + const originalRef = await originalCell.resolveTextModelRef(); + const modifiedRef = await modifiedCell.resolveTextModelRef(); + const textModel = originalRef.object.textEditorModel; + const modifiedTextModel = modifiedRef.object.textEditorModel; + this._register(originalRef); + this._register(modifiedRef); + + this._editor.setModel({ + original: textModel, + modified: modifiedTextModel + }); + + const contentHeight = this._editor.getContentHeight(); + + this._editorContainer.style.height = `${contentHeight}px`; + this._editor.layout(); + this.notebookEditor.layoutNotebookCell(this.cell, contentHeight + 32); + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel.ts b/src/vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel.ts index 67392c463ae..ce5c353ed71 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel.ts @@ -4,12 +4,27 @@ *--------------------------------------------------------------------------------------------*/ import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; +import { NotebookDiffEditorEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; +import { Emitter } from 'vs/base/common/event'; +import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { CellDiffViewModelLayoutChangeEvent } from 'vs/workbench/contrib/notebook/browser/diff/common'; + +export class CellDiffViewModel extends Disposable { + private _layoutInfoEmitter = new Emitter(); + + onDidLayoutChange = this._layoutInfoEmitter.event; -export class CellDiffViewModel { constructor( readonly original: NotebookCellTextModel | undefined, readonly modified: NotebookCellTextModel | undefined, - readonly type: 'unchanged' | 'insert' | 'delete' | 'modified' + readonly type: 'unchanged' | 'insert' | 'delete' | 'modified', + readonly editorEventDispatcher: NotebookDiffEditorEventDispatcher ) { + super(); + + this._register(this.editorEventDispatcher.onDidChangeLayout(e => { + this._layoutInfoEmitter.fire({ outerWidth: e.value.width }); + })); } } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/common.ts b/src/vs/workbench/contrib/notebook/browser/diff/common.ts index e4cb954fbf1..fd6ac29ca3d 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/common.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/common.ts @@ -6,9 +6,20 @@ import { NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellDiffViewModel } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; import { Event } from 'vs/base/common/event'; +import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; export interface INotebookTextDiffEditor { onMouseUp: Event<{ readonly event: MouseEvent; readonly target: CellDiffViewModel; }>; getLayoutInfo(): NotebookLayoutInfo; layoutNotebookCell(cell: CellDiffViewModel, height: number): void; } + +export interface CellDiffRenderTemplate { + readonly container: HTMLElement; + +} + +export interface CellDiffViewModelLayoutChangeEvent { + font?: BareFontInfo; + outerWidth?: number; +} diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index 421f4868d68..faf32eab803 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -29,6 +29,7 @@ import { NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebo import { INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/common'; import { Emitter } from 'vs/base/common/event'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { NotebookDiffEditorEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; export class NotebookTextDiffEditor extends BaseEditor implements INotebookTextDiffEditor { static readonly ID: string = 'workbench.editor.notebookTextDiffEditor'; @@ -40,6 +41,7 @@ export class NotebookTextDiffEditor extends BaseEditor implements INotebookTextD private readonly _onMouseUp = this._register(new Emitter<{ readonly event: MouseEvent; readonly target: CellDiffViewModel; }>()); public readonly onMouseUp = this._onMouseUp.event; + private _eventDispatcher: NotebookDiffEditorEventDispatcher | undefined; constructor( @IInstantiationService readonly instantiationService: IInstantiationService, @@ -129,6 +131,8 @@ export class NotebookTextDiffEditor extends BaseEditor implements INotebookTextD return; } + this._eventDispatcher = new NotebookDiffEditorEventDispatcher(); + const diffResult = await this.notebookEditorWorkerService.computeDiff(model.original.resource, model.modified.resource); const cellChanges = diffResult.cellsDiff.changes; @@ -146,7 +150,8 @@ export class NotebookTextDiffEditor extends BaseEditor implements INotebookTextD return new CellDiffViewModel( cell, undefined, - 'unchanged' + 'unchanged', + this._eventDispatcher! ); })); @@ -157,7 +162,8 @@ export class NotebookTextDiffEditor extends BaseEditor implements INotebookTextD cellDiffViewModels.push(new CellDiffViewModel( originalModel.cells[change.originalStart + j], modifiedModel.cells[change.modifiedStart + j], - 'modified' + 'modified', + this._eventDispatcher! )); } @@ -166,7 +172,8 @@ export class NotebookTextDiffEditor extends BaseEditor implements INotebookTextD cellDiffViewModels.push(new CellDiffViewModel( originalModel.cells[change.originalStart + j], undefined, - 'delete' + 'delete', + this._eventDispatcher! )); } @@ -175,7 +182,8 @@ export class NotebookTextDiffEditor extends BaseEditor implements INotebookTextD cellDiffViewModels.push(new CellDiffViewModel( undefined, modifiedModel.cells[change.modifiedStart + j], - 'insert' + 'insert', + this._eventDispatcher! )); } @@ -187,7 +195,8 @@ export class NotebookTextDiffEditor extends BaseEditor implements INotebookTextD cellDiffViewModels.push(new CellDiffViewModel( originalModel.cells[i], undefined, - 'delete' + 'delete', + this._eventDispatcher! )); } @@ -195,7 +204,8 @@ export class NotebookTextDiffEditor extends BaseEditor implements INotebookTextD cellDiffViewModels.push(new CellDiffViewModel( undefined, modifiedModel.cells[i], - 'insert' + 'insert', + this._eventDispatcher! )); } @@ -271,6 +281,7 @@ export class NotebookTextDiffEditor extends BaseEditor implements INotebookTextD this._rootElement.style.height = `${dimension.height}px`; this._list?.layout(this._dimension.height, this._dimension.width); + this._eventDispatcher?.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]); } } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts index b16fe0f6686..97646bae5d2 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts @@ -7,8 +7,7 @@ import 'vs/css!./notebookDiff'; import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import * as DOM from 'vs/base/browser/dom'; import { IListStyles, IStyleController } from 'vs/base/browser/ui/list/listWidget'; -import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; -import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -16,73 +15,9 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IListService, IWorkbenchListOptions, WorkbenchList } from 'vs/platform/list/browser/listService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { CellDiffViewModel } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; -import { INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/common'; -import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; +import { CellDiffRenderTemplate, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/common'; import { isMacintosh } from 'vs/base/common/platform'; -import { renderCodicons } from 'vs/base/common/codicons'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { IModeService } from 'vs/editor/common/services/modeService'; -import { format } from 'vs/base/common/jsonFormatter'; -import { applyEdits } from 'vs/base/common/jsonEdit'; - -export interface CellDiffRenderTemplate { - readonly container: HTMLElement; - -} - -const fixedDiffEditorOptions: IEditorOptions = { - padding: { - top: 12, - bottom: 12 - }, - scrollBeyondLastLine: false, - scrollbar: { - verticalScrollbarSize: 14, - horizontal: 'auto', - useShadows: true, - verticalHasArrows: false, - horizontalHasArrows: false, - alwaysConsumeMouseWheel: false - }, - renderLineHighlightOnlyWhenFocus: true, - overviewRulerLanes: 0, - selectOnLineNumbers: false, - wordWrap: 'off', - lineNumbers: 'off', - lineDecorationsWidth: 0, - glyphMargin: true, - fixedOverflowWidgets: true, - minimap: { enabled: false }, - renderValidationDecorations: 'on' -}; - -const fixedEditorOptions: IEditorOptions = { - padding: { - top: 12, - bottom: 12 - }, - scrollBeyondLastLine: false, - scrollbar: { - verticalScrollbarSize: 14, - horizontal: 'auto', - useShadows: true, - verticalHasArrows: false, - horizontalHasArrows: false, - alwaysConsumeMouseWheel: false - }, - renderLineHighlightOnlyWhenFocus: true, - overviewRulerLanes: 0, - selectOnLineNumbers: false, - wordWrap: 'off', - lineNumbers: 'off', - lineDecorationsWidth: 0, - glyphMargin: false, - fixedOverflowWidgets: true, - minimap: { enabled: false }, - renderValidationDecorations: 'on' -}; +import { UnchangedCell, DeletedCell, InsertCell, ModifiedCell } from 'vs/workbench/contrib/notebook/browser/diff/cellComponents'; export class NotebookCellTextDiffListDelegate implements IListVirtualDelegate { // private readonly lineHeight: number; @@ -149,371 +84,6 @@ export class CellDiffRenderer implements IListRenderer { - if (e.contentHeightChanged) { - this._layoutInfo.editorHeight = e.contentHeight; - this.layout({ editorHeight: true }); - } - })); - - originalCell.resolveTextModelRef().then(ref => { - this._register(ref); - - const textModel = ref.object.textEditorModel; - this._editor.setModel(textModel); - - this._layoutInfo.editorHeight = this._editor.getContentHeight(); - this.layout({ editorHeight: true }); - }); - } - - layout(state: { editorHeight?: boolean, metadataEditor?: boolean }) { - if (state.editorHeight) { - this._editor.layout({ - width: this.notebookEditor.getLayoutInfo().width - 20, - height: this._layoutInfo.editorHeight - }); - } - - if (state.metadataEditor) { - this._metadataEditor?.layout({ - width: this.notebookEditor.getLayoutInfo().width - 20, - height: this._layoutInfo.metadataHeight - }); - } - this.notebookEditor.layoutNotebookCell(this.cell, - this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight); - } - - private _updateFoldingIcon() { - if (this._foldingState === MetadataFoldingState.Collapsed) { - this._foldingIndicator.innerHTML = renderCodicons('$(chevron-right)'); - } else { - this._foldingIndicator.innerHTML = renderCodicons('$(chevron-down)'); - } - } - - buildMetadata() { - this._foldingIndicator = DOM.append(this._metadataHeaderContainer, DOM.$('.metadata-folding-indicator')); - this._updateFoldingIcon(); - const metadataStatus = DOM.append(this._metadataHeaderContainer, DOM.$('div.metadata-status')); - const metadataStatusSpan = DOM.append(metadataStatus, DOM.$('span')); - metadataStatusSpan.textContent = 'Metadata unchanged'; - - this._register(this.notebookEditor.onMouseUp(e => { - if (!e.event.target) { - return; - } - - const target = e.event.target as HTMLElement; - - if (DOM.hasClass(target, 'codicon-chevron-down') || DOM.hasClass(target, 'codicon-chevron-right')) { - const parent = target.parentElement as HTMLElement; - - if (parent && !DOM.hasClass(parent, 'metadata-folding-indicator')) { - return; - } - - // folding icon - - const cellViewModel = e.target; - - if (cellViewModel === this.cell) { - this._foldingState = this._foldingState === MetadataFoldingState.Expanded ? MetadataFoldingState.Collapsed : MetadataFoldingState.Expanded; - this.updateMetadataRendering(); - } - } - - return; - })); - - this.updateMetadataRendering(); - } - - updateMetadataRendering() { - if (this._foldingState === MetadataFoldingState.Expanded) { - // we should expand the metadata editor - this._metadataInfoContainer.style.display = 'block'; - - if (!this._metadataEditorContainer || !this._metadataEditor) { - // create editor - this._metadataEditorContainer = DOM.append(this._metadataInfoContainer, DOM.$('.metadata-editor-container')); - - this._metadataEditor = this.instantiationService.createInstance(CodeEditorWidget, this._metadataEditorContainer, { - ...fixedEditorOptions, - dimension: { - width: this.notebookEditor.getLayoutInfo().width - 20, - height: 0 - } - }, {}); - - const mode = this.modeService.create('json'); - const content = JSON.stringify(this.cell.original!.metadata); - const edits = format(content, undefined, {}); - const metadataSource = applyEdits(content, edits); - const metadataModel = this.modelService.createModel(metadataSource, mode, undefined, true); - this._metadataEditor.setModel(metadataModel); - - this._layoutInfo.metadataHeight = this._metadataEditor.getContentHeight(); - console.log(this._layoutInfo.metadataHeight); - this.layout({ metadataEditor: true }); - - this._register(this._metadataEditor.onDidContentSizeChange((e) => { - if (e.contentHeightChanged && this._foldingState === MetadataFoldingState.Expanded) { - this._layoutInfo.metadataHeight = e.contentHeight; - this.layout({ metadataEditor: true }); - } - })); - } else { - this._layoutInfo.metadataHeight = this._metadataEditor.getContentHeight(); - this.layout({ metadataEditor: true }); - } - } else { - // we should collapse the metadata editor - this._metadataInfoContainer.style.display = 'none'; - this._metadataEditorDisposeStore.clear(); - this._layoutInfo.metadataHeight = 0; - this.layout({}); - } - - this._updateFoldingIcon(); - - } -} - -class DeletedCell extends Disposable { - private _editor!: CodeEditorWidget; - constructor( - readonly notebookEditor: INotebookTextDiffEditor, - readonly cell: CellDiffViewModel, - readonly templateData: CellDiffRenderTemplate, - @IInstantiationService protected readonly instantiationService: IInstantiationService, - ) { - super(); - const diffEditorContainer = DOM.$('.cell-diff-editor-container'); - DOM.append(templateData.container, diffEditorContainer); - DOM.addClass(diffEditorContainer, 'delete'); - - const originalCell = cell.original!; - const lineCount = originalCell.textBuffer.getLineCount(); - const lineHeight = notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; - const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; - const editorContainer = DOM.append(diffEditorContainer, DOM.$('.editor-container')); - const diagonalFill = DOM.append(diffEditorContainer, DOM.$('.diagonal-fill')); - - this._editor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, { - ...fixedEditorOptions, - dimension: { - width: (notebookEditor.getLayoutInfo().width - 20) / 2 - 18, - height: editorHeight - } - }, {}); - - diagonalFill.style.height = `${editorHeight}px`; - - this._register(this._editor.onDidContentSizeChange((e) => { - if (e.contentHeightChanged) { - this._editor.layout({ - width: (notebookEditor.getLayoutInfo().width - 20) / 2 - 18, - height: e.contentHeight - }); - diagonalFill.style.height = `${e.contentHeight}px`; - this.notebookEditor.layoutNotebookCell(this.cell, e.contentHeight + 32); - } - })); - - originalCell.resolveTextModelRef().then(ref => { - this._register(ref); - - const textModel = ref.object.textEditorModel; - this._editor.setModel(textModel); - - this.notebookEditor.layoutNotebookCell(this.cell, this._editor.getContentHeight() + 32); - diagonalFill.style.height = `${this._editor.getContentHeight()}px`; - - }); - } -} - -class InsertCell extends Disposable { - private _editor!: CodeEditorWidget; - constructor( - readonly notebookEditor: INotebookTextDiffEditor, - readonly cell: CellDiffViewModel, - readonly templateData: CellDiffRenderTemplate, - @IInstantiationService protected readonly instantiationService: IInstantiationService, - ) { - super(); - const diffEditorContainer = DOM.$('.cell-diff-editor-container'); - DOM.append(templateData.container, diffEditorContainer); - DOM.addClass(diffEditorContainer, 'insert'); - - const modifiedCell = cell.modified!; - const lineCount = modifiedCell.textBuffer.getLineCount(); - const lineHeight = notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; - const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; - const diagonalFill = DOM.append(diffEditorContainer, DOM.$('.diagonal-fill')); - const editorContainer = DOM.append(diffEditorContainer, DOM.$('.editor-container')); - - this._editor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, { - ...fixedEditorOptions, - dimension: { - width: (notebookEditor.getLayoutInfo().width - 20) / 2 - 18, - height: editorHeight - } - }, {}); - - diagonalFill.style.height = `${editorHeight}px`; - - this._register(this._editor.onDidContentSizeChange((e) => { - if (e.contentHeightChanged) { - this._editor.layout({ - width: (notebookEditor.getLayoutInfo().width - 20) / 2 - 18, - height: e.contentHeight - }); - diagonalFill.style.height = `${e.contentHeight}px`; - this.notebookEditor.layoutNotebookCell(this.cell, e.contentHeight + 32); - } - })); - - modifiedCell.resolveTextModelRef().then(ref => { - this._register(ref); - - const textModel = ref.object.textEditorModel; - this._editor.setModel(textModel); - - this.notebookEditor.layoutNotebookCell(this.cell, this._editor.getContentHeight() + 32); - diagonalFill.style.height = `${this._editor.getContentHeight()}px`; - }); - } -} - -class ModifiedCell extends Disposable { - private _editor!: DiffEditorWidget; - private _editorContainer!: HTMLElement; - constructor( - readonly notebookEditor: INotebookTextDiffEditor, - readonly cell: CellDiffViewModel, - readonly templateData: CellDiffRenderTemplate, - @IInstantiationService protected readonly instantiationService: IInstantiationService, - ) { - super(); - const diffEditorContainer = DOM.$('.cell-diff-editor-container'); - DOM.append(templateData.container, diffEditorContainer); - - const modifiedCell = cell.modified!; - const lineCount = modifiedCell.textBuffer.getLineCount(); - const lineHeight = notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; - const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; - this._editorContainer = DOM.append(diffEditorContainer, DOM.$('.editor-container')); - - this._editor = this.instantiationService.createInstance(DiffEditorWidget, this._editorContainer, { - ...fixedDiffEditorOptions - }); - - this._editor.layout({ - width: notebookEditor.getLayoutInfo().width - 20, - height: editorHeight - }); - - this._editorContainer.style.height = `${editorHeight}px`; - - this._register(this._editor.onDidContentSizeChange((e) => { - if (e.contentHeightChanged) { - this._editorContainer.style.height = `${e.contentHeight}px`; - this.notebookEditor.layoutNotebookCell(this.cell, e.contentHeight + 32); - this._editor.layout(); - } - })); - - this.initialize(); - } - - async initialize() { - const originalCell = this.cell.original!; - const modifiedCell = this.cell.modified!; - - const originalRef = await originalCell.resolveTextModelRef(); - const modifiedRef = await modifiedCell.resolveTextModelRef(); - const textModel = originalRef.object.textEditorModel; - const modifiedTextModel = modifiedRef.object.textEditorModel; - this._register(originalRef); - this._register(modifiedRef); - - this._editor.setModel({ - original: textModel, - modified: modifiedTextModel - }); - - const contentHeight = this._editor.getContentHeight(); - - this._editorContainer.style.height = `${contentHeight}px`; - this._editor.layout(); - this.notebookEditor.layoutNotebookCell(this.cell, contentHeight + 32); - } -} export class NotebookTextDiffList extends WorkbenchList implements IDisposable, IStyleController { private styleElement?: HTMLStyleElement; @@ -528,9 +98,7 @@ export class NotebookTextDiffList extends WorkbenchList imple @IListService listService: IListService, @IThemeService themeService: IThemeService, @IConfigurationService configurationService: IConfigurationService, - @IKeybindingService keybindingService: IKeybindingService, - @IInstantiationService instantiationService: IInstantiationService - ) { + @IKeybindingService keybindingService: IKeybindingService) { super(listUser, container, delegate, renderers, options, contextKeyService, listService, themeService, configurationService, keybindingService); } diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher.ts index 8700ba68862..bca810545ec 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher.ts @@ -70,3 +70,24 @@ export class NotebookEventDispatcher { } } } + +export class NotebookDiffEditorEventDispatcher { + protected readonly _onDidChangeLayout = new Emitter(); + readonly onDidChangeLayout = this._onDidChangeLayout.event; + + constructor() { + } + + emit(events: NotebookViewEvent[]) { + for (let i = 0, len = events.length; i < len; i++) { + const e = events[i]; + + switch (e.type) { + case NotebookViewEventType.LayoutChanged: + this._onDidChangeLayout.fire(e); + break; + } + } + } + +} From d4f6fe322726b458024cd9dc712487b0f5c4d52a Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 21 Aug 2020 10:09:04 -0700 Subject: [PATCH 426/736] notebooks: deprecate viewType for renderers, prefer renderer ID --- extensions/vscode-notebook-tests/package.json | 2 +- .../contrib/notebook/browser/extensionPoint.ts | 13 ++++++++++--- .../contrib/notebook/browser/notebookServiceImpl.ts | 10 +++++++--- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/extensions/vscode-notebook-tests/package.json b/extensions/vscode-notebook-tests/package.json index 3479ad57701..53ba12569a9 100644 --- a/extensions/vscode-notebook-tests/package.json +++ b/extensions/vscode-notebook-tests/package.json @@ -62,7 +62,7 @@ ], "notebookOutputRenderer": [ { - "viewType": "notebookCoreTestRenderer", + "id": "notebookCoreTestRenderer", "displayName": "Notebook Core Test Renderer", "entrypoint": "./src/customRenderer.js", "mimeTypes": [ diff --git a/src/vs/workbench/contrib/notebook/browser/extensionPoint.ts b/src/vs/workbench/contrib/notebook/browser/extensionPoint.ts index a2945c71589..f9b0f8bd14d 100644 --- a/src/vs/workbench/contrib/notebook/browser/extensionPoint.ts +++ b/src/vs/workbench/contrib/notebook/browser/extensionPoint.ts @@ -25,13 +25,15 @@ export interface INotebookEditorContribution { namespace NotebookRendererContribution { export const viewType = 'viewType'; + export const id = 'id'; export const displayName = 'displayName'; export const mimeTypes = 'mimeTypes'; export const entrypoint = 'entrypoint'; } export interface INotebookRendererContribution { - readonly [NotebookRendererContribution.viewType]: string; + readonly [NotebookRendererContribution.id]?: string; + readonly [NotebookRendererContribution.viewType]?: string; readonly [NotebookRendererContribution.displayName]: string; readonly [NotebookRendererContribution.mimeTypes]?: readonly string[]; readonly [NotebookRendererContribution.entrypoint]: string; @@ -94,18 +96,23 @@ const notebookProviderContribution: IJSONSchema = { const notebookRendererContribution: IJSONSchema = { description: nls.localize('contributes.notebook.renderer', 'Contributes notebook output renderer provider.'), type: 'array', - defaultSnippets: [{ body: [{ viewType: '', displayName: '', mimeTypes: [''] }] }], + defaultSnippets: [{ body: [{ id: '', displayName: '', mimeTypes: [''] }] }], items: { type: 'object', required: [ - NotebookRendererContribution.viewType, + NotebookRendererContribution.id, NotebookRendererContribution.displayName, NotebookRendererContribution.mimeTypes, NotebookRendererContribution.entrypoint, ], properties: { + [NotebookRendererContribution.id]: { + type: 'string', + description: nls.localize('contributes.notebook.renderer.viewType', 'Unique identifier of the notebook output renderer.'), + }, [NotebookRendererContribution.viewType]: { type: 'string', + deprecationMessage: nls.localize('contributes.notebook.provider.viewType.deprecated', 'Rename `viewType` to `id`.'), description: nls.localize('contributes.notebook.renderer.viewType', 'Unique identifier of the notebook output renderer.'), }, [NotebookRendererContribution.displayName]: { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 7e0fb78f8aa..4fdbe29a434 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -293,8 +293,14 @@ export class NotebookService extends Disposable implements INotebookService, ICu continue; } + const id = notebookContribution.id ?? notebookContribution.viewType; + if (!id) { + console.error(`Notebook renderer from ${extension.description.identifier.value} is missing an 'id'`); + continue; + } + this.notebookRenderersInfoStore.add(new NotebookOutputRendererInfo({ - id: notebookContribution.viewType, + id, extension: extension.description, entrypoint: notebookContribution.entrypoint, displayName: notebookContribution.displayName, @@ -302,8 +308,6 @@ export class NotebookService extends Disposable implements INotebookService, ICu })); } } - - // console.log(this.notebookRenderersInfoStore); }); this._editorService.registerCustomEditorViewTypesHandler('Notebook', this); From 3cc3de4d6823bb6456fb2d47532e687449f22be3 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 21 Aug 2020 10:12:43 -0700 Subject: [PATCH 427/736] Make fold indicator hit target larger --- .../contrib/notebook/browser/media/notebook.css | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index fcf4355f7e1..4e23c35c249 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -727,16 +727,21 @@ } .monaco-workbench .notebookOverlay > .cell-list-container .notebook-folding-indicator { + height: 20px; + width: 20px; + position: absolute; - top: 0; - left: 0; - right: 0; - height: 100%; + top: 6px; + left: 8px; + display: flex; + justify-content: center; + align-items: center; + z-index: 26; } .monaco-workbench .notebookOverlay > .cell-list-container .notebook-folding-indicator .codicon { visibility: visible; - padding: 8px 0 0 10px; + height: 16px; } /** Theming */ From 6d4ecda76c8f7ae756f5f8aebb6e1895547608e8 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 21 Aug 2020 10:43:29 -0700 Subject: [PATCH 428/736] Fix cell output height messed up after editing gh cell --- .../browser/view/renderers/codeCell.ts | 67 ++++++++++--------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index 03b24d3ca06..0a305a7d9d5 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -324,7 +324,8 @@ export class CodeCell extends Disposable { if (renderedOutput) { if (renderedOutput.renderResult.type !== RenderOutputType.None) { // Show inset in webview, or render output that isn't rendered - this.renderOutput(currOutput, index, undefined); + // TODO@roblou skipHeightInit flag is a hack - the webview only sends the real height once. Don't wipe it out here. + this.renderOutput(currOutput, index, undefined, true); } else { // Anything else, just update the height this.viewCell.updateOutputHeight(index, renderedOutput.element.clientHeight); @@ -429,7 +430,7 @@ export class CodeCell extends Disposable { ); } - private renderOutput(currOutput: IProcessedOutput, index: number, beforeElement?: HTMLElement) { + private renderOutput(currOutput: IProcessedOutput, index: number, beforeElement?: HTMLElement, skipHeightInit = false) { if (!this.outputResizeListeners.has(currOutput)) { this.outputResizeListeners.set(currOutput, new DisposableStore()); } @@ -509,40 +510,42 @@ export class CodeCell extends Disposable { outputItemDiv.style.position = 'absolute'; } - if (outputHasDynamicHeight(result)) { - this.viewCell.selfSizeMonitoring = true; + if (!skipHeightInit) { + if (outputHasDynamicHeight(result)) { + this.viewCell.selfSizeMonitoring = true; - const clientHeight = outputItemDiv.clientHeight; - const dimension = { - width: this.viewCell.layoutInfo.editorWidth, - height: clientHeight - }; - const elementSizeObserver = getResizesObserver(outputItemDiv, dimension, () => { - if (this.templateData.outputContainer && document.body.contains(this.templateData.outputContainer!)) { - const height = Math.ceil(elementSizeObserver.getHeight()); + const clientHeight = outputItemDiv.clientHeight; + const dimension = { + width: this.viewCell.layoutInfo.editorWidth, + height: clientHeight + }; + const elementSizeObserver = getResizesObserver(outputItemDiv, dimension, () => { + if (this.templateData.outputContainer && document.body.contains(this.templateData.outputContainer!)) { + const height = Math.ceil(elementSizeObserver.getHeight()); - if (clientHeight === height) { - return; + if (clientHeight === height) { + return; + } + + const currIndex = this.viewCell.outputs.indexOf(currOutput); + if (currIndex < 0) { + return; + } + + this.viewCell.updateOutputHeight(currIndex, height); + this.relayoutCell(); } + }); + elementSizeObserver.startObserving(); + this.outputResizeListeners.get(currOutput)!.add(elementSizeObserver); + this.viewCell.updateOutputHeight(index, clientHeight); + } else if (result.type !== RenderOutputType.None) { // no-op if it's a webview + const clientHeight = Math.ceil(outputItemDiv.clientHeight); + this.viewCell.updateOutputHeight(index, clientHeight); - const currIndex = this.viewCell.outputs.indexOf(currOutput); - if (currIndex < 0) { - return; - } - - this.viewCell.updateOutputHeight(currIndex, height); - this.relayoutCell(); - } - }); - elementSizeObserver.startObserving(); - this.outputResizeListeners.get(currOutput)!.add(elementSizeObserver); - this.viewCell.updateOutputHeight(index, clientHeight); - } else if (result.type !== RenderOutputType.None) { // no-op if it's a webview - const clientHeight = Math.ceil(outputItemDiv.clientHeight); - this.viewCell.updateOutputHeight(index, clientHeight); - - const top = this.viewCell.getOutputOffsetInContainer(index); - outputItemDiv.style.top = `${top}px`; + const top = this.viewCell.getOutputOffsetInContainer(index); + outputItemDiv.style.top = `${top}px`; + } } } From 2c713dd105fe1d0883527c9bc865b35e8676cea5 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 21 Aug 2020 10:49:32 -0700 Subject: [PATCH 429/736] "Clear cell output" should clear execution state #103713 --- .../contrib/notebook/browser/contrib/coreActions.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index ee6efac4503..687c6e3c154 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -1291,6 +1291,14 @@ registerAction2(class extends NotebookCellAction { } editor.viewModel.notebookDocument.clearCellOutput(context.cell.handle); + if (context.cell.metadata && context.cell.metadata?.runState !== NotebookCellRunState.Running) { + context.notebookEditor.viewModel!.notebookDocument.changeCellMetadata(context.cell.handle, { + runState: NotebookCellRunState.Idle, + runStartTime: undefined, + lastRunDuration: undefined, + statusMessage: undefined + }); + } } }); From 59b97e190081aa3eaf5a6f766b4dbdd603a7a749 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 21 Aug 2020 10:50:36 -0700 Subject: [PATCH 430/736] Just "clear cell outputs", not "active cell outputs" --- .../workbench/contrib/notebook/browser/contrib/coreActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 687c6e3c154..7c41f1bf8c3 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -1268,7 +1268,7 @@ registerAction2(class extends NotebookCellAction { constructor() { super({ id: CLEAR_CELL_OUTPUTS_COMMAND_ID, - title: localize('clearActiveCellOutputs', 'Clear Active Cell Outputs'), + title: localize('clearCellOutputs', 'Clear Cell Outputs'), menu: { id: MenuId.NotebookCellTitle, when: ContextKeyExpr.and(NOTEBOOK_CELL_TYPE.isEqualTo('code'), NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_CELL_HAS_OUTPUTS), From 0bb98bf6866c5abe2fd7d42d70eb67091b0c125a Mon Sep 17 00:00:00 2001 From: Rob OLeary Date: Sat, 22 Aug 2020 02:07:24 +0800 Subject: [PATCH 431/736] Add more snippets for basic syntax (#105174) --- .../snippets/markdown.code-snippets | 60 ++++++++++++------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/extensions/markdown-basics/snippets/markdown.code-snippets b/extensions/markdown-basics/snippets/markdown.code-snippets index 6ee831ae4a5..b07f984138c 100644 --- a/extensions/markdown-basics/snippets/markdown.code-snippets +++ b/extensions/markdown-basics/snippets/markdown.code-snippets @@ -14,43 +14,54 @@ "body": "> ${1:${TM_SELECTED_TEXT}}", "description": "Insert quoted text" }, - "Insert code": { + "Insert inline code": { "prefix": "code", "body": "`${1:${TM_SELECTED_TEXT}}`$0", - "description": "Insert code" + "description": "Insert inline code" }, "Insert fenced code block": { "prefix": "fenced codeblock", - "body": [ - "```${1:language}", - "${TM_SELECTED_TEXT}$0", - "```" - ], + "body": ["```${1:language}", "${TM_SELECTED_TEXT}$0", "```"], "description": "Insert fenced code block" }, - "Insert heading": { - "prefix": "heading", + "Insert heading level 1": { + "prefix": "heading1", "body": "# ${1:${TM_SELECTED_TEXT}}", - "description": "Insert heading" + "description": "Insert heading level 1" + }, + "Insert heading level 2": { + "prefix": "heading2", + "body": "## ${1:${TM_SELECTED_TEXT}}", + "description": "Insert heading level 2" + }, + "Insert heading level 3": { + "prefix": "heading3", + "body": "### ${1:${TM_SELECTED_TEXT}}", + "description": "Insert heading level 3" + }, + "Insert heading level 4": { + "prefix": "heading4", + "body": "#### ${1:${TM_SELECTED_TEXT}}", + "description": "Insert heading level 4" + }, + "Insert heading level 5": { + "prefix": "heading5", + "body": "##### ${1:${TM_SELECTED_TEXT}}", + "description": "Insert heading level 5" + }, + "Insert heading level 6": { + "prefix": "heading6", + "body": "###### ${1:${TM_SELECTED_TEXT}}", + "description": "Insert heading level 6" }, "Insert unordered list": { "prefix": "unordered list", - "body": [ - "- ${1:first}", - "- ${2:second}", - "- ${3:third}", - "$0" - ], + "body": ["- ${1:first}", "- ${2:second}", "- ${3:third}", "$0"], "description": "Insert unordered list" }, "Insert ordered list": { "prefix": "ordered list", - "body": [ - "1. ${1:first}", - "2. ${2:second}", - "3. ${3:third}", - "$0" - ], + "body": ["1. ${1:first}", "2. ${2:second}", "3. ${3:third}", "$0"], "description": "Insert ordered list" }, "Insert horizontal rule": { @@ -67,5 +78,10 @@ "prefix": "image", "body": "![${TM_SELECTED_TEXT:${1:alt}}](https://${2:link})$0", "description": "Insert image" + }, + "Insert strikethrough": { + "prefix": "strikethrough", + "body": "~~${1:${TM_SELECTED_TEXT}}~~", + "description": "Insert strikethrough" } } From ab2ba17b4055b7498128757175e2943314708fba Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 21 Aug 2020 20:13:43 +0200 Subject: [PATCH 432/736] debug: only decorate glyph arrow for focused session fixes #105139 --- .../browser/callStackEditorContribution.ts | 25 +++++++++++-------- .../debug/test/browser/callStack.test.ts | 6 ++--- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts index 023fa7edd57..b0c25ff46dd 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts @@ -40,7 +40,7 @@ const FOCUSED_STACK_FRAME_DECORATION: IModelDecorationOptions = { stickiness }; -export function createDecorationsForStackFrame(stackFrame: IStackFrame, topStackFrameRange: IRange | undefined): IModelDeltaDecoration[] { +export function createDecorationsForStackFrame(stackFrame: IStackFrame, topStackFrameRange: IRange | undefined, isFocusedSession: boolean): IModelDeltaDecoration[] { // only show decorations for the currently focused thread. const result: IModelDeltaDecoration[] = []; const columnUntilEOLRange = new Range(stackFrame.range.startLineNumber, stackFrame.range.startColumn, stackFrame.range.startLineNumber, Constants.MAX_SAFE_SMALL_INTEGER); @@ -50,10 +50,12 @@ export function createDecorationsForStackFrame(stackFrame: IStackFrame, topStack // an exception or a stack frame that did not change the line number (we only decorate the columns, not the whole line). const topStackFrame = stackFrame.thread.getTopStackFrame(); if (stackFrame.getId() === topStackFrame?.getId()) { - result.push({ - options: TOP_STACK_FRAME_MARGIN, - range - }); + if (isFocusedSession) { + result.push({ + options: TOP_STACK_FRAME_MARGIN, + range + }); + } result.push({ options: TOP_STACK_FRAME_DECORATION, @@ -68,10 +70,12 @@ export function createDecorationsForStackFrame(stackFrame: IStackFrame, topStack } topStackFrameRange = columnUntilEOLRange; } else { - result.push({ - options: FOCUSED_STACK_FRAME_MARGIN, - range - }); + if (isFocusedSession) { + result.push({ + options: FOCUSED_STACK_FRAME_MARGIN, + range + }); + } result.push({ options: FOCUSED_STACK_FRAME_DECORATION, @@ -106,6 +110,7 @@ export class CallStackEditorContribution implements IEditorContribution { const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame; const decorations: IModelDeltaDecoration[] = []; this.debugService.getModel().getSessions().forEach(s => { + const isSessionFocused = s === focusedStackFrame?.thread.session; s.getAllThreads().forEach(t => { if (t.stopped) { let candidateStackFrame = t === focusedStackFrame?.thread ? focusedStackFrame : undefined; @@ -117,7 +122,7 @@ export class CallStackEditorContribution implements IEditorContribution { } if (candidateStackFrame && candidateStackFrame.source.uri.toString() === this.editor.getModel()?.uri.toString()) { - decorations.push(...createDecorationsForStackFrame(candidateStackFrame, this.topStackFrameRange)); + decorations.push(...createDecorationsForStackFrame(candidateStackFrame, this.topStackFrameRange, isSessionFocused)); } } }); diff --git a/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts b/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts index 9449af5368e..596687ed5a0 100644 --- a/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts @@ -297,7 +297,7 @@ suite('Debug - CallStack', () => { const session = createMockSession(model); model.addSession(session); const { firstStackFrame, secondStackFrame } = createTwoStackFrames(session); - let decorations = createDecorationsForStackFrame(firstStackFrame, firstStackFrame.range); + let decorations = createDecorationsForStackFrame(firstStackFrame, firstStackFrame.range, true); assert.equal(decorations.length, 2); assert.deepEqual(decorations[0].range, new Range(1, 2, 1, 1)); assert.equal(decorations[0].options.glyphMarginClassName, 'codicon-debug-stackframe'); @@ -305,7 +305,7 @@ suite('Debug - CallStack', () => { assert.equal(decorations[1].options.className, 'debug-top-stack-frame-line'); assert.equal(decorations[1].options.isWholeLine, true); - decorations = createDecorationsForStackFrame(secondStackFrame, firstStackFrame.range); + decorations = createDecorationsForStackFrame(secondStackFrame, firstStackFrame.range, true); assert.equal(decorations.length, 2); assert.deepEqual(decorations[0].range, new Range(1, 2, 1, 1)); assert.equal(decorations[0].options.glyphMarginClassName, 'codicon-debug-stackframe-focused'); @@ -313,7 +313,7 @@ suite('Debug - CallStack', () => { assert.equal(decorations[1].options.className, 'debug-focused-stack-frame-line'); assert.equal(decorations[1].options.isWholeLine, true); - decorations = createDecorationsForStackFrame(firstStackFrame, new Range(1, 5, 1, 6)); + decorations = createDecorationsForStackFrame(firstStackFrame, new Range(1, 5, 1, 6), true); assert.equal(decorations.length, 3); assert.deepEqual(decorations[0].range, new Range(1, 2, 1, 1)); assert.equal(decorations[0].options.glyphMarginClassName, 'codicon-debug-stackframe'); From 312abad5a057220527fd739ef04875f256a835fe Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 21 Aug 2020 20:23:59 +0200 Subject: [PATCH 433/736] Revert "debug: do not show toolbar while initialising" This reverts commit 135b6da83746477b2983fee879122ceefa70014d. --- .../contrib/debug/browser/callStackView.ts | 4 ++-- .../contrib/debug/browser/debugToolBar.ts | 2 +- .../contrib/debug/browser/debugViewlet.ts | 15 +++++---------- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 301ac0bf63e..23009759915 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -982,10 +982,10 @@ function getActions(instantiationService: IInstantiationService, element: IDebug } -export class StopAction extends Action { +class StopAction extends Action { constructor( - private readonly session: IDebugSession | null, + private readonly session: IDebugSession, @ICommandService private readonly commandService: ICommandService ) { super(`action.${STOP_ID}`, STOP_LABEL, 'debug-action codicon-debug-stop'); diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index 6658ae4b1f5..54c7593b579 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -91,7 +91,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { this.updateScheduler = this._register(new RunOnceScheduler(() => { const state = this.debugService.state; const toolBarLocation = this.configurationService.getValue('debug').toolBarLocation; - if (state === State.Inactive || state === State.Initializing || toolBarLocation === 'docked' || toolBarLocation === 'hidden') { + if (state === State.Inactive || toolBarLocation === 'docked' || toolBarLocation === 'hidden') { return this.hide(); } diff --git a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts index d20d93a8fac..d32774028e5 100644 --- a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts @@ -34,7 +34,6 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { ShowViewletAction } from 'vs/workbench/browser/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { StopAction } from 'vs/workbench/contrib/debug/browser/callStackView'; export class DebugViewPaneContainer extends ViewPaneContainer { @@ -66,7 +65,9 @@ export class DebugViewPaneContainer extends ViewPaneContainer { super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService); this.updateToolBarScheduler = this._register(new RunOnceScheduler(() => { - this.updateTitleArea(); + if (this.configurationService.getValue('debug').toolBarLocation === 'docked') { + this.updateTitleArea(); + } }, 20)); // When there are potential updates to the docked debug toolbar we need to update it @@ -118,11 +119,6 @@ export class DebugViewPaneContainer extends ViewPaneContainer { return this._register(this.instantiationService.createInstance(OpenDebugConsoleAction, OpenDebugConsoleAction.ID, OpenDebugConsoleAction.LABEL)); } - @memoize - private get stopAction(): StopAction { - return this._register(this.instantiationService.createInstance(StopAction, null)); - } - @memoize private get selectAndStartAction(): SelectAndStartAction { return this._register(this.instantiationService.createInstance(SelectAndStartAction, SelectAndStartAction.ID, nls.localize('startAdditionalSession', "Start Additional Session"))); @@ -154,13 +150,12 @@ export class DebugViewPaneContainer extends ViewPaneContainer { return [this.toggleReplAction]; } - const firstAction = this.debugService.state === State.Initializing ? this.stopAction : this.startAction; - return [firstAction, this.configureAction, this.toggleReplAction]; + return [this.startAction, this.configureAction, this.toggleReplAction]; } get showInitialDebugActions(): boolean { const state = this.debugService.state; - return state === State.Inactive || state === State.Initializing || this.configurationService.getValue('debug').toolBarLocation !== 'docked'; + return state === State.Inactive || this.configurationService.getValue('debug').toolBarLocation !== 'docked'; } getSecondaryActions(): IAction[] { From 84af9182013c4f1d18ac6ebc55ec91d1940b478e Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 21 Aug 2020 20:27:09 +0200 Subject: [PATCH 434/736] debug: toolbar disable pause action and close quick pick #84228 --- .../workbench/contrib/debug/browser/debug.contribution.ts | 2 +- src/vs/workbench/contrib/debug/browser/debugService.ts | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 7f0411f6483..5e9db371cae 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -158,7 +158,7 @@ function registerCommandsAndActions(): void { }; registerDebugToolBarItem(CONTINUE_ID, CONTINUE_LABEL, 10, { id: 'codicon/debug-continue' }, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); - registerDebugToolBarItem(PAUSE_ID, PAUSE_LABEL, 10, { id: 'codicon/debug-pause' }, CONTEXT_DEBUG_STATE.notEqualsTo('stopped')); + registerDebugToolBarItem(PAUSE_ID, PAUSE_LABEL, 10, { id: 'codicon/debug-pause' }, CONTEXT_DEBUG_STATE.notEqualsTo('stopped'), CONTEXT_DEBUG_STATE.isEqualTo('running')); registerDebugToolBarItem(STOP_ID, STOP_LABEL, 70, { id: 'codicon/debug-stop' }, CONTEXT_FOCUSED_SESSION_IS_ATTACH.toNegated()); registerDebugToolBarItem(DISCONNECT_ID, DISCONNECT_LABEL, 70, { id: 'codicon/debug-disconnect' }, CONTEXT_FOCUSED_SESSION_IS_ATTACH); registerDebugToolBarItem(STEP_OVER_ID, STEP_OVER_LABEL, 20, { id: 'codicon/debug-step-over' }, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index fa3df30dc4d..898d4f0ab82 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -47,6 +47,7 @@ import { DebugStorage } from 'vs/workbench/contrib/debug/common/debugStorage'; import { DebugTelemetry } from 'vs/workbench/contrib/debug/common/debugTelemetry'; import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompoundRoot'; import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; export class DebugService implements IDebugService { declare readonly _serviceBrand: undefined; @@ -90,7 +91,8 @@ export class DebugService implements IDebugService { @IConfigurationService private readonly configurationService: IConfigurationService, @IExtensionHostDebugService private readonly extensionHostDebugService: IExtensionHostDebugService, @IActivityService private readonly activityService: IActivityService, - @ICommandService private readonly commandService: ICommandService + @ICommandService private readonly commandService: ICommandService, + @IQuickInputService private readonly quickInputService: IQuickInputService ) { this.toDispose = []; @@ -732,7 +734,7 @@ export class DebugService implements IDebugService { }); } - stopSession(session: IDebugSession | undefined): Promise { + async stopSession(session: IDebugSession | undefined): Promise { if (session) { return session.terminate(); } @@ -740,6 +742,8 @@ export class DebugService implements IDebugService { const sessions = this.model.getSessions(); if (sessions.length === 0) { this.taskRunner.cancel(); + // User might have cancelled starting of a debug session, and in some cases the quick pick is left open + await this.quickInputService.cancel(); this.endInitializingState(); this.cancelTokens(undefined); } From e906097398eb92af6cd523f8a3b91317234d0030 Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 21 Aug 2020 11:47:00 -0700 Subject: [PATCH 435/736] shared layoutInfo. --- .../notebook/browser/diff/cellComponents.ts | 359 +++++++++++------- .../notebook/browser/diff/notebookDiff.css | 8 +- .../browser/diff/notebookTextDiffEditor.ts | 4 +- 3 files changed, 223 insertions(+), 148 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index 9445d33056a..2f6ca1f3c5c 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -77,34 +77,63 @@ enum MetadataFoldingState { } abstract class AbstractCellRenderer extends Disposable { + protected _metadataHeaderContainer!: HTMLElement; + protected _metadataInfoContainer!: HTMLElement; + protected _layoutInfo!: { + editorHeight: number; + editorMargin: number; + metadataStatusHeight: number; + metadataHeight: number; + }; + constructor( readonly notebookEditor: INotebookTextDiffEditor, readonly cell: CellDiffViewModel, + readonly templateData: CellDiffRenderTemplate, + protected readonly instantiationService: IInstantiationService, ) { super(); - + // init + this._layoutInfo = { + editorHeight: 0, + editorMargin: 32, + metadataHeight: 0, + metadataStatusHeight: 24 + }; + this.initData(); + this.buildBody(templateData.container); this._register(cell.onDidLayoutChange(e => this.onDidLayoutChange(e))); } + buildBody(container: HTMLElement) { + const diffEditorContainer = DOM.$('.cell-diff-editor-container'); + DOM.append(container, diffEditorContainer); + this.styleContainer(diffEditorContainer); + const sourceContainer = DOM.append(diffEditorContainer, DOM.$('.source-container')); + this.buildSourceEditor(sourceContainer); + + this._metadataHeaderContainer = DOM.append(diffEditorContainer, DOM.$('.metadata-header-container')); + this._metadataInfoContainer = DOM.append(diffEditorContainer, DOM.$('.metadata-info-container')); + this.buildMetadataHeader(this._metadataHeaderContainer); + this.buildMetadataBody(this._metadataInfoContainer); + } + + abstract initData(): void; + abstract styleContainer(container: HTMLElement): void; + abstract buildSourceEditor(sourceContainer: HTMLElement): void; + abstract buildMetadataHeader(metadataHeaderContainer: HTMLElement): void; + abstract buildMetadataBody(metadataBodyContainer: HTMLElement): void; + abstract onDidLayoutChange(event: CellDiffViewModelLayoutChangeEvent): void; } export class UnchangedCell extends AbstractCellRenderer { private _editor!: CodeEditorWidget; private _metadataEditor?: CodeEditorWidget; - private _layoutInfo!: { - editorHeight: number; - editorMargin: number; - metadataStatusHeight: number; - metadataHeight: number; - }; - - private _foldingState: MetadataFoldingState; - private _metadataHeaderContainer!: HTMLElement; - private _metadataInfoContainer!: HTMLElement; + private _foldingState!: MetadataFoldingState; private _metadataEditorContainer?: HTMLElement; private _foldingIndicator!: HTMLElement; - private _metadataEditorDisposeStore = new DisposableStore(); + private _metadataEditorDisposeStore!: DisposableStore; constructor( readonly notebookEditor: INotebookTextDiffEditor, @@ -114,34 +143,28 @@ export class UnchangedCell extends AbstractCellRenderer { @IModelService protected modelService: IModelService, @IInstantiationService protected readonly instantiationService: IInstantiationService, ) { - super(notebookEditor, cell); - - // init - this._layoutInfo = { - editorHeight: 0, - editorMargin: 32, - metadataHeight: 0, - metadataStatusHeight: 24 - }; + super(notebookEditor, cell, templateData, instantiationService); + } + initData() { this._foldingState = MetadataFoldingState.Collapsed; + this._metadataEditorDisposeStore = new DisposableStore(); + } - const diffEditorContainer = DOM.$('.cell-diff-editor-container'); - DOM.append(templateData.container, diffEditorContainer); + styleContainer(container: HTMLElement) { + } - const originalCell = cell.original!; + buildSourceEditor(sourceContainer: HTMLElement) { + const editorContainer = DOM.append(sourceContainer, DOM.$('.editor-container')); + const originalCell = this.cell.original!; const lineCount = originalCell.textBuffer.getLineCount(); - const lineHeight = notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; + const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; - const editorContainer = DOM.append(diffEditorContainer, DOM.$('.editor-container')); - this._metadataHeaderContainer = DOM.append(diffEditorContainer, DOM.$('.metadata-container')); - this._metadataInfoContainer = DOM.append(diffEditorContainer, DOM.$('.metadata-info-container')); - this.buildMetadata(); this._editor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, { ...fixedEditorOptions, dimension: { - width: notebookEditor.getLayoutInfo().width - 20, + width: this.notebookEditor.getLayoutInfo().width - 20, height: editorHeight } }, {}); @@ -164,6 +187,46 @@ export class UnchangedCell extends AbstractCellRenderer { }); } + buildMetadataHeader(metadataHeaderContainer: HTMLElement): void { + this._foldingIndicator = DOM.append(metadataHeaderContainer, DOM.$('.metadata-folding-indicator')); + this._updateFoldingIcon(); + const metadataStatus = DOM.append(metadataHeaderContainer, DOM.$('div.metadata-status')); + const metadataStatusSpan = DOM.append(metadataStatus, DOM.$('span')); + metadataStatusSpan.textContent = 'Metadata unchanged'; + + this._register(this.notebookEditor.onMouseUp(e => { + if (!e.event.target) { + return; + } + + const target = e.event.target as HTMLElement; + + if (DOM.hasClass(target, 'codicon-chevron-down') || DOM.hasClass(target, 'codicon-chevron-right')) { + const parent = target.parentElement as HTMLElement; + + if (parent && !DOM.hasClass(parent, 'metadata-folding-indicator')) { + return; + } + + // folding icon + + const cellViewModel = e.target; + + if (cellViewModel === this.cell) { + this._foldingState = this._foldingState === MetadataFoldingState.Expanded ? MetadataFoldingState.Collapsed : MetadataFoldingState.Expanded; + this.updateMetadataRendering(); + } + } + + return; + })); + + this.updateMetadataRendering(); + } + buildMetadataBody(metadataBodyContainer: HTMLElement): void { + + } + onDidLayoutChange(event: CellDiffViewModelLayoutChangeEvent): void { if (event.outerWidth !== undefined) { this.layout({ outerWidth: true }); @@ -196,41 +259,7 @@ export class UnchangedCell extends AbstractCellRenderer { } } - buildMetadata() { - this._foldingIndicator = DOM.append(this._metadataHeaderContainer, DOM.$('.metadata-folding-indicator')); - this._updateFoldingIcon(); - const metadataStatus = DOM.append(this._metadataHeaderContainer, DOM.$('div.metadata-status')); - const metadataStatusSpan = DOM.append(metadataStatus, DOM.$('span')); - metadataStatusSpan.textContent = 'Metadata unchanged'; - - this._register(this.notebookEditor.onMouseUp(e => { - if (!e.event.target) { - return; - } - - const target = e.event.target as HTMLElement; - - if (DOM.hasClass(target, 'codicon-chevron-down') || DOM.hasClass(target, 'codicon-chevron-right')) { - const parent = target.parentElement as HTMLElement; - - if (parent && !DOM.hasClass(parent, 'metadata-folding-indicator')) { - return; - } - - // folding icon - - const cellViewModel = e.target; - - if (cellViewModel === this.cell) { - this._foldingState = this._foldingState === MetadataFoldingState.Expanded ? MetadataFoldingState.Collapsed : MetadataFoldingState.Expanded; - this.updateMetadataRendering(); - } - } - - return; - })); - - this.updateMetadataRendering(); + buildMetadata(diffEditorContainer: HTMLElement) { } updateMetadataRendering() { @@ -258,7 +287,6 @@ export class UnchangedCell extends AbstractCellRenderer { this._metadataEditor.setModel(metadataModel); this._layoutInfo.metadataHeight = this._metadataEditor.getContentHeight(); - console.log(this._layoutInfo.metadataHeight); this.layout({ metadataEditor: true }); this._register(this._metadataEditor.onDidContentSizeChange((e) => { @@ -286,42 +314,46 @@ export class UnchangedCell extends AbstractCellRenderer { export class DeletedCell extends AbstractCellRenderer { private _editor!: CodeEditorWidget; + private _diagonalFill!: HTMLElement; constructor( readonly notebookEditor: INotebookTextDiffEditor, readonly cell: CellDiffViewModel, readonly templateData: CellDiffRenderTemplate, @IInstantiationService protected readonly instantiationService: IInstantiationService, ) { - super(notebookEditor, cell); - const diffEditorContainer = DOM.$('.cell-diff-editor-container'); - DOM.append(templateData.container, diffEditorContainer); - DOM.addClass(diffEditorContainer, 'delete'); + super(notebookEditor, cell, templateData, instantiationService); + } - const originalCell = cell.original!; + initData(): void { + } + + styleContainer(container: HTMLElement) { + DOM.addClass(container, 'delete'); + } + + buildSourceEditor(sourceContainer: HTMLElement): void { + const originalCell = this.cell.original!; const lineCount = originalCell.textBuffer.getLineCount(); - const lineHeight = notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; + const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; - const editorContainer = DOM.append(diffEditorContainer, DOM.$('.editor-container')); - const diagonalFill = DOM.append(diffEditorContainer, DOM.$('.diagonal-fill')); + + const editorContainer = DOM.append(sourceContainer, DOM.$('.editor-container')); + this._diagonalFill = DOM.append(sourceContainer, DOM.$('.diagonal-fill')); this._editor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, { ...fixedEditorOptions, dimension: { - width: (notebookEditor.getLayoutInfo().width - 20) / 2 - 18, + width: (this.notebookEditor.getLayoutInfo().width - 20) / 2 - 18, height: editorHeight } }, {}); - diagonalFill.style.height = `${editorHeight}px`; + this._diagonalFill.style.height = `${editorHeight}px`; this._register(this._editor.onDidContentSizeChange((e) => { if (e.contentHeightChanged) { - this._editor.layout({ - width: (notebookEditor.getLayoutInfo().width - 20) / 2 - 18, - height: e.contentHeight - }); - diagonalFill.style.height = `${e.contentHeight}px`; - this.notebookEditor.layoutNotebookCell(this.cell, e.contentHeight + 32); + this._layoutInfo.editorHeight = e.contentHeight; + this.layout({ editorHeight: true }); } })); @@ -330,69 +362,78 @@ export class DeletedCell extends AbstractCellRenderer { const textModel = ref.object.textEditorModel; this._editor.setModel(textModel); - - this.notebookEditor.layoutNotebookCell(this.cell, this._editor.getContentHeight() + 32); - diagonalFill.style.height = `${this._editor.getContentHeight()}px`; + this._layoutInfo.editorHeight = this._editor.getContentHeight(); + this.layout({ editorHeight: true }); }); + + } + buildMetadataHeader(metadataHeaderContainer: HTMLElement): void { + } + + buildMetadataBody(metadataBodyContainer: HTMLElement): void { } onDidLayoutChange(e: CellDiffViewModelLayoutChangeEvent) { if (e.outerWidth !== undefined) { + this.layout({ outerWidth: true }); + } + } + layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean }) { + if (state.editorHeight || state.outerWidth) { this._editor.layout({ width: (this.notebookEditor.getLayoutInfo().width - 20) / 2 - 18, - height: this._editor.getContentHeight() + height: this._layoutInfo.editorHeight }); } + + this._diagonalFill.style.height = `${this._layoutInfo.editorHeight}px`; + + this.notebookEditor.layoutNotebookCell(this.cell, + this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight); } } export class InsertCell extends AbstractCellRenderer { private _editor!: CodeEditorWidget; + private _diagonalFill!: HTMLElement; constructor( readonly notebookEditor: INotebookTextDiffEditor, readonly cell: CellDiffViewModel, readonly templateData: CellDiffRenderTemplate, @IInstantiationService protected readonly instantiationService: IInstantiationService, ) { - super(notebookEditor, cell); - const diffEditorContainer = DOM.$('.cell-diff-editor-container'); - DOM.append(templateData.container, diffEditorContainer); - DOM.addClass(diffEditorContainer, 'insert'); + super(notebookEditor, cell, templateData, instantiationService); + } - const modifiedCell = cell.modified!; + initData(): void { + } + + styleContainer(container: HTMLElement): void { + DOM.addClass(container, 'insert'); + } + + buildSourceEditor(sourceContainer: HTMLElement): void { + const modifiedCell = this.cell.modified!; const lineCount = modifiedCell.textBuffer.getLineCount(); - const lineHeight = notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; + const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; - const diagonalFill = DOM.append(diffEditorContainer, DOM.$('.diagonal-fill')); - const editorContainer = DOM.append(diffEditorContainer, DOM.$('.editor-container')); + this._diagonalFill = DOM.append(sourceContainer, DOM.$('.diagonal-fill')); + const editorContainer = DOM.append(sourceContainer, DOM.$('.editor-container')); this._editor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, { ...fixedEditorOptions, dimension: { - width: (notebookEditor.getLayoutInfo().width - 20) / 2 - 18, + width: (this.notebookEditor.getLayoutInfo().width - 20) / 2 - 18, height: editorHeight } }, {}); - diagonalFill.style.height = `${editorHeight}px`; + this._diagonalFill.style.height = `${editorHeight}px`; this._register(this._editor.onDidContentSizeChange((e) => { if (e.contentHeightChanged) { - this._editor.layout({ - width: (notebookEditor.getLayoutInfo().width - 20) / 2 - 18, - height: e.contentHeight - }); - diagonalFill.style.height = `${e.contentHeight}px`; - this.notebookEditor.layoutNotebookCell(this.cell, e.contentHeight + 32); - } - })); - - this._register(this.cell.onDidLayoutChange(e => { - if (e.outerWidth !== undefined) { - this._editor.layout({ - width: (notebookEditor.getLayoutInfo().width - 20) / 2 - 18, - height: this._editor.getContentHeight() - }); + this._layoutInfo.editorHeight = e.contentHeight; + this.layout({ editorHeight: true }); } })); @@ -401,24 +442,40 @@ export class InsertCell extends AbstractCellRenderer { const textModel = ref.object.textEditorModel; this._editor.setModel(textModel); - - this.notebookEditor.layoutNotebookCell(this.cell, this._editor.getContentHeight() + 32); - diagonalFill.style.height = `${this._editor.getContentHeight()}px`; + this._layoutInfo.editorHeight = this._editor.getContentHeight(); + this.layout({ editorHeight: true }); }); } + buildMetadataHeader(metadataHeaderContainer: HTMLElement): void { + } + + buildMetadataBody(metadataBodyContainer: HTMLElement): void { + } + onDidLayoutChange(e: CellDiffViewModelLayoutChangeEvent) { if (e.outerWidth !== undefined) { + this.layout({ outerWidth: true }); + } + } + + layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean }) { + if (state.editorHeight || state.outerWidth) { this._editor.layout({ width: (this.notebookEditor.getLayoutInfo().width - 20) / 2 - 18, - height: this._editor.getContentHeight() + height: this._layoutInfo.editorHeight }); } + + this._diagonalFill.style.height = `${this._layoutInfo.editorHeight}px`; + + this.notebookEditor.layoutNotebookCell(this.cell, + this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight); } } export class ModifiedCell extends AbstractCellRenderer { - private _editor!: DiffEditorWidget; + private _editor?: DiffEditorWidget; private _editorContainer!: HTMLElement; constructor( readonly notebookEditor: INotebookTextDiffEditor, @@ -426,22 +483,28 @@ export class ModifiedCell extends AbstractCellRenderer { readonly templateData: CellDiffRenderTemplate, @IInstantiationService protected readonly instantiationService: IInstantiationService, ) { - super(notebookEditor, cell); - const diffEditorContainer = DOM.$('.cell-diff-editor-container'); - DOM.append(templateData.container, diffEditorContainer); + super(notebookEditor, cell, templateData, instantiationService); + } - const modifiedCell = cell.modified!; + initData(): void { + } + + styleContainer(container: HTMLElement): void { + } + + buildSourceEditor(sourceContainer: HTMLElement): void { + const modifiedCell = this.cell.modified!; const lineCount = modifiedCell.textBuffer.getLineCount(); - const lineHeight = notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; + const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; - this._editorContainer = DOM.append(diffEditorContainer, DOM.$('.editor-container')); + this._editorContainer = DOM.append(sourceContainer, DOM.$('.editor-container')); this._editor = this.instantiationService.createInstance(DiffEditorWidget, this._editorContainer, { ...fixedDiffEditorOptions }); this._editor.layout({ - width: notebookEditor.getLayoutInfo().width - 20, + width: this.notebookEditor.getLayoutInfo().width - 20, height: editorHeight }); @@ -449,25 +512,15 @@ export class ModifiedCell extends AbstractCellRenderer { this._register(this._editor.onDidContentSizeChange((e) => { if (e.contentHeightChanged) { - this._editorContainer.style.height = `${e.contentHeight}px`; - this.notebookEditor.layoutNotebookCell(this.cell, e.contentHeight + 32); - this._editor.layout(); + this._layoutInfo.editorHeight = e.contentHeight; + this.layout({ editorHeight: true }); } })); - this.initialize(); + this._initializeSourceDiffEditor(); } - onDidLayoutChange(e: CellDiffViewModelLayoutChangeEvent) { - if (e.outerWidth !== undefined) { - this._editor.layout({ - width: this.notebookEditor.getLayoutInfo().width - 20, - height: this._editor.getContentHeight() - }); - } - } - - async initialize() { + private async _initializeSourceDiffEditor() { const originalCell = this.cell.original!; const modifiedCell = this.cell.modified!; @@ -478,15 +531,37 @@ export class ModifiedCell extends AbstractCellRenderer { this._register(originalRef); this._register(modifiedRef); - this._editor.setModel({ + this._editor!.setModel({ original: textModel, modified: modifiedTextModel }); - const contentHeight = this._editor.getContentHeight(); + const contentHeight = this._editor!.getContentHeight(); + this._layoutInfo.editorHeight = contentHeight; + this.layout({ editorHeight: true }); + + } + + buildMetadataHeader(metadataHeaderContainer: HTMLElement): void { + } + + buildMetadataBody(metadataBodyContainer: HTMLElement): void { + } + + onDidLayoutChange(e: CellDiffViewModelLayoutChangeEvent) { + if (e.outerWidth !== undefined) { + this.layout({ outerWidth: true }); + } + } + + layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean }) { + if (state.editorHeight || state.outerWidth) { + this._editorContainer.style.height = `${this._layoutInfo.editorHeight}px`; + this._editor!.layout(); + } + + this.notebookEditor.layoutNotebookCell(this.cell, + this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight); - this._editorContainer.style.height = `${contentHeight}px`; - this._editor.layout(); - this.notebookEditor.layoutNotebookCell(this.cell, contentHeight + 32); } } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css index 6f4897cf5ea..7b023ccc64c 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css @@ -20,24 +20,24 @@ margin: 8px; } -.notebook-text-diff-editor .cell-diff-editor-container .metadata-container { +.notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container { display: flex; height: 24px; align-items: center; cursor: default; } -.notebook-text-diff-editor .cell-diff-editor-container .metadata-container .metadata-folding-indicator .codicon { +.notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .metadata-folding-indicator .codicon { visibility: visible; padding: 4px 0 0 10px; cursor: pointer; } -.notebook-text-diff-editor .cell-diff-editor-container .metadata-container .metadata-status { +.notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .metadata-status { font-size: 12px; } -.notebook-text-diff-editor .cell-diff-editor-container .metadata-container .metadata-status span { +.notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .metadata-status span { margin: 0 8px; line-height: 21px; } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index faf32eab803..d6fb2853758 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -294,7 +294,7 @@ registerThemingParticipant((theme, collector) => { border-right: 1px solid ${cellBorderColor}; border-bottom: 1px solid ${cellBorderColor}; }`); - collector.addRule(`.notebook-text-diff-editor .cell-diff-editor-container .metadata-container { + collector.addRule(`.notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container { border-left: 1px solid ${cellBorderColor}; border-right: 1px solid ${cellBorderColor}; border-bottom: 1px solid ${cellBorderColor}; @@ -317,7 +317,7 @@ registerThemingParticipant((theme, collector) => { const containerBackground = theme.getColor(notebookOutputContainerColor); if (containerBackground) { - collector.addRule(`.notebook-text-diff-editor .cell-diff-editor-container .metadata-container { background-color: ${containerBackground}; }`); + collector.addRule(`.notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container { background-color: ${containerBackground}; }`); } }); From 70573c9674e9a801e8f63667ec5f78d9ee6efbbd Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 21 Aug 2020 20:59:03 +0200 Subject: [PATCH 436/736] get rid of treeFilter since there is no re-use --- .../contrib/debug/browser/media/repl.css | 37 ++++ .../workbench/contrib/debug/browser/repl.ts | 3 +- .../contrib/debug/browser/replFilter.ts | 184 +++++++++++++++++ .../contrib/markers/browser/media/markers.css | 2 +- .../treeFilter/browser/media/treeFilter.css | 41 ---- .../treeFilter/browser/treeFilterView.ts | 189 ------------------ 6 files changed, 223 insertions(+), 233 deletions(-) delete mode 100644 src/vs/workbench/contrib/treeFilter/browser/media/treeFilter.css delete mode 100644 src/vs/workbench/contrib/treeFilter/browser/treeFilterView.ts diff --git a/src/vs/workbench/contrib/debug/browser/media/repl.css b/src/vs/workbench/contrib/debug/browser/media/repl.css index b9599c28628..adfebd7645e 100644 --- a/src/vs/workbench/contrib/debug/browser/media/repl.css +++ b/src/vs/workbench/contrib/debug/browser/media/repl.css @@ -87,3 +87,40 @@ .monaco-workbench .repl .repl-tree .output.expression .code-bold { font-weight: bold; } .monaco-workbench .repl .repl-tree .output.expression .code-italic { font-style: italic; } .monaco-workbench .repl .repl-tree .output.expression .code-underline { text-decoration: underline; } + +.monaco-action-bar .action-item.panel-action-tree-filter-container { + cursor: default; + display: flex; +} + +.monaco-action-bar .panel-action-tree-filter{ + display: flex; + align-items: center; + flex: 1; +} + +.monaco-action-bar .panel-action-tree-filter .monaco-inputbox { + height: 24px; + font-size: 12px; + flex: 1; +} + +.pane-header .monaco-action-bar .panel-action-tree-filter .monaco-inputbox { + height: 20px; + line-height: 18px; +} + +.monaco-workbench.vs .monaco-action-bar .panel-action-tree-filter .monaco-inputbox { + height: 25px; +} + +.panel > .title .monaco-action-bar .action-item.panel-action-tree-filter-container { + max-width: 400px; + min-width: 300px; + margin-right: 10px; +} + +.monaco-action-bar .action-item.panel-action-tree-filter-container, +.panel > .title .monaco-action-bar .action-item.panel-action-tree-filter-container.grow { + flex: 1; +} diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 5ec4fa0aa67..85f739834b5 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -59,8 +59,7 @@ import { ReplGroup } from 'vs/workbench/contrib/debug/common/replModel'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { EDITOR_FONT_DEFAULTS, EditorOption } from 'vs/editor/common/config/editorOptions'; import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; -import { TreeFilterPanelActionViewItem, TreeFilterState } from 'vs/workbench/contrib/treeFilter/browser/treeFilterView'; -import { ReplFilter } from 'vs/workbench/contrib/debug/browser/replFilter'; +import { ReplFilter, TreeFilterState, TreeFilterPanelActionViewItem } from 'vs/workbench/contrib/debug/browser/replFilter'; const $ = dom.$; diff --git a/src/vs/workbench/contrib/debug/browser/replFilter.ts b/src/vs/workbench/contrib/debug/browser/replFilter.ts index 243c10f8d6a..e5675cf2bab 100644 --- a/src/vs/workbench/contrib/debug/browser/replFilter.ts +++ b/src/vs/workbench/contrib/debug/browser/replFilter.ts @@ -8,6 +8,21 @@ import { splitGlobAware } from 'vs/base/common/glob'; import * as strings from 'vs/base/common/strings'; import { ITreeFilter, TreeVisibility, TreeFilterResult } from 'vs/base/browser/ui/tree/tree'; import { IReplElement } from 'vs/workbench/contrib/debug/common/debug'; +import * as DOM from 'vs/base/browser/dom'; +import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { Delayer } from 'vs/base/common/async'; +import { IAction } from 'vs/base/common/actions'; +import { HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { toDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { Event, Emitter } from 'vs/base/common/event'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { ContextScopedHistoryInputBox } from 'vs/platform/browser/contextScopedHistoryWidget'; +import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; + type ParsedQuery = { type: 'include' | 'exclude', @@ -60,3 +75,172 @@ export class ReplFilter implements ITreeFilter { return includeQueryPresent ? includeQueryMatched : parentVisibility; } } + +export interface IReplFiltersChangeEvent { + filterText?: boolean; + layout?: boolean; +} + +export interface IReplFiltersOptions { + filterText: string; + filterHistory: string[]; + layout: DOM.Dimension; +} + +export class TreeFilterState extends Disposable { + + private readonly _onDidChange: Emitter = this._register(new Emitter()); + readonly onDidChange: Event = this._onDidChange.event; + + constructor(options: IReplFiltersOptions) { + super(); + this._filterText = options.filterText; + this.filterHistory = options.filterHistory; + this._layout = options.layout; + } + + private _filterText: string; + get filterText(): string { + return this._filterText; + } + set filterText(filterText: string) { + if (this._filterText !== filterText) { + this._filterText = filterText; + this._onDidChange.fire({ filterText: true }); + } + } + + filterHistory: string[]; + + private _layout: DOM.Dimension = new DOM.Dimension(0, 0); + get layout(): DOM.Dimension { + return this._layout; + } + set layout(layout: DOM.Dimension) { + if (this._layout.width !== layout.width || this._layout.height !== layout.height) { + this._layout = layout; + this._onDidChange.fire({ layout: true }); + } + } +} + +export class TreeFilterPanelActionViewItem extends BaseActionViewItem { + + private delayedFilterUpdate: Delayer; + private container: HTMLElement | undefined; + private filterInputBox: HistoryInputBox | undefined; + + constructor( + action: IAction, + private placeholder: string, + private filters: TreeFilterState, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IThemeService private readonly themeService: IThemeService, + @IContextViewService private readonly contextViewService: IContextViewService) { + super(null, action); + this.delayedFilterUpdate = new Delayer(200); + this._register(toDisposable(() => this.delayedFilterUpdate.cancel())); + } + + render(container: HTMLElement): void { + this.container = container; + DOM.addClass(this.container, 'panel-action-tree-filter-container'); + + this.element = DOM.append(this.container, DOM.$('')); + this.element.className = this.class; + this.createInput(this.element); + this.updateClass(); + + this.adjustInputBox(); + } + + focus(): void { + if (this.filterInputBox) { + this.filterInputBox.focus(); + } + } + + private clearFilterText(): void { + if (this.filterInputBox) { + this.filterInputBox.value = ''; + } + } + + private createInput(container: HTMLElement): void { + this.filterInputBox = this._register(this.instantiationService.createInstance(ContextScopedHistoryInputBox, container, this.contextViewService, { + placeholder: this.placeholder, + history: this.filters.filterHistory + })); + this._register(attachInputBoxStyler(this.filterInputBox, this.themeService)); + this.filterInputBox.value = this.filters.filterText; + this._register(this.filterInputBox.onDidChange(() => this.delayedFilterUpdate.trigger(() => this.onDidInputChange(this.filterInputBox!)))); + this._register(this.filters.onDidChange((event: IReplFiltersChangeEvent) => { + if (event.filterText) { + this.filterInputBox!.value = this.filters.filterText; + } + })); + this._register(DOM.addStandardDisposableListener(this.filterInputBox.inputElement, DOM.EventType.KEY_DOWN, (e: any) => this.onInputKeyDown(e))); + this._register(DOM.addStandardDisposableListener(container, DOM.EventType.KEY_DOWN, this.handleKeyboardEvent)); + this._register(DOM.addStandardDisposableListener(container, DOM.EventType.KEY_UP, this.handleKeyboardEvent)); + this._register(DOM.addStandardDisposableListener(this.filterInputBox.inputElement, DOM.EventType.CLICK, (e) => { + e.stopPropagation(); + e.preventDefault(); + })); + this._register(this.filters.onDidChange(e => this.onDidFiltersChange(e))); + } + + private onDidFiltersChange(e: IReplFiltersChangeEvent): void { + if (e.layout) { + this.updateClass(); + } + } + + private onDidInputChange(inputbox: HistoryInputBox) { + inputbox.addToHistory(); + this.filters.filterText = inputbox.value; + this.filters.filterHistory = inputbox.getHistory(); + } + + // Action toolbar is swallowing some keys for action items which should not be for an input box + private handleKeyboardEvent(event: StandardKeyboardEvent) { + if (event.equals(KeyCode.Space) + || event.equals(KeyCode.LeftArrow) + || event.equals(KeyCode.RightArrow) + || event.equals(KeyCode.Escape) + ) { + event.stopPropagation(); + } + } + + private onInputKeyDown(event: StandardKeyboardEvent) { + if (event.equals(KeyCode.Escape)) { + this.clearFilterText(); + event.stopPropagation(); + event.preventDefault(); + } + } + + private adjustInputBox(): void { + if (this.element && this.filterInputBox) { + this.filterInputBox.inputElement.style.paddingRight = DOM.hasClass(this.element, 'small') ? '25px' : '150px'; + } + } + + protected updateClass(): void { + if (this.element && this.container) { + this.element.className = this.class; + DOM.toggleClass(this.container, 'grow', DOM.hasClass(this.element, 'grow')); + this.adjustInputBox(); + } + } + + protected get class(): string { + if (this.filters.layout.width > 800) { + return 'panel-action-tree-filter grow'; + } else if (this.filters.layout.width < 600) { + return 'panel-action-tree-filter small'; + } else { + return 'panel-action-tree-filter'; + } + } +} diff --git a/src/vs/workbench/contrib/markers/browser/media/markers.css b/src/vs/workbench/contrib/markers/browser/media/markers.css index b898358d421..ec967357971 100644 --- a/src/vs/workbench/contrib/markers/browser/media/markers.css +++ b/src/vs/workbench/contrib/markers/browser/media/markers.css @@ -65,7 +65,7 @@ } .panel > .title .monaco-action-bar .action-item.markers-panel-action-filter-container { - max-width: 600px; + max-width: 400px; min-width: 300px; margin-right: 10px; } diff --git a/src/vs/workbench/contrib/treeFilter/browser/media/treeFilter.css b/src/vs/workbench/contrib/treeFilter/browser/media/treeFilter.css deleted file mode 100644 index 30c22462797..00000000000 --- a/src/vs/workbench/contrib/treeFilter/browser/media/treeFilter.css +++ /dev/null @@ -1,41 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -.monaco-action-bar .action-item.panel-action-tree-filter-container { - cursor: default; - display: flex; -} - -.monaco-action-bar .panel-action-tree-filter{ - display: flex; - align-items: center; - flex: 1; -} - -.monaco-action-bar .panel-action-tree-filter .monaco-inputbox { - height: 24px; - font-size: 12px; - flex: 1; -} - -.pane-header .monaco-action-bar .panel-action-tree-filter .monaco-inputbox { - height: 20px; - line-height: 18px; -} - -.monaco-workbench.vs .monaco-action-bar .panel-action-tree-filter .monaco-inputbox { - height: 25px; -} - -.panel > .title .monaco-action-bar .action-item.panel-action-tree-filter-container { - max-width: 600px; - min-width: 300px; - margin-right: 10px; -} - -.monaco-action-bar .action-item.panel-action-tree-filter-container, -.panel > .title .monaco-action-bar .action-item.panel-action-tree-filter-container.grow { - flex: 1; -} diff --git a/src/vs/workbench/contrib/treeFilter/browser/treeFilterView.ts b/src/vs/workbench/contrib/treeFilter/browser/treeFilterView.ts deleted file mode 100644 index 87b785171ff..00000000000 --- a/src/vs/workbench/contrib/treeFilter/browser/treeFilterView.ts +++ /dev/null @@ -1,189 +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/treeFilter'; -import * as DOM from 'vs/base/browser/dom'; -import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; -import { Delayer } from 'vs/base/common/async'; -import { IAction } from 'vs/base/common/actions'; -import { HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { toDisposable, Disposable } from 'vs/base/common/lifecycle'; -import { Event, Emitter } from 'vs/base/common/event'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { KeyCode } from 'vs/base/common/keyCodes'; -import { ContextScopedHistoryInputBox } from 'vs/platform/browser/contextScopedHistoryWidget'; -import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; - -export interface IReplFiltersChangeEvent { - filterText?: boolean; - layout?: boolean; -} - -export interface IReplFiltersOptions { - filterText: string; - filterHistory: string[]; - layout: DOM.Dimension; -} - -export class TreeFilterState extends Disposable { - - private readonly _onDidChange: Emitter = this._register(new Emitter()); - readonly onDidChange: Event = this._onDidChange.event; - - constructor(options: IReplFiltersOptions) { - super(); - this._filterText = options.filterText; - this.filterHistory = options.filterHistory; - this._layout = options.layout; - } - - private _filterText: string; - get filterText(): string { - return this._filterText; - } - set filterText(filterText: string) { - if (this._filterText !== filterText) { - this._filterText = filterText; - this._onDidChange.fire({ filterText: true }); - } - } - - filterHistory: string[]; - - private _layout: DOM.Dimension = new DOM.Dimension(0, 0); - get layout(): DOM.Dimension { - return this._layout; - } - set layout(layout: DOM.Dimension) { - if (this._layout.width !== layout.width || this._layout.height !== layout.height) { - this._layout = layout; - this._onDidChange.fire({ layout: true }); - } - } -} - -export class TreeFilterPanelActionViewItem extends BaseActionViewItem { - - private delayedFilterUpdate: Delayer; - private container: HTMLElement | undefined; - private filterInputBox: HistoryInputBox | undefined; - - constructor( - action: IAction, - private placeholder: string, - private filters: TreeFilterState, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IThemeService private readonly themeService: IThemeService, - @IContextViewService private readonly contextViewService: IContextViewService) { - super(null, action); - this.delayedFilterUpdate = new Delayer(200); - this._register(toDisposable(() => this.delayedFilterUpdate.cancel())); - } - - render(container: HTMLElement): void { - this.container = container; - DOM.addClass(this.container, 'panel-action-tree-filter-container'); - - this.element = DOM.append(this.container, DOM.$('')); - this.element.className = this.class; - this.createInput(this.element); - this.updateClass(); - - this.adjustInputBox(); - } - - focus(): void { - if (this.filterInputBox) { - this.filterInputBox.focus(); - } - } - - private clearFilterText(): void { - if (this.filterInputBox) { - this.filterInputBox.value = ''; - } - } - - private createInput(container: HTMLElement): void { - this.filterInputBox = this._register(this.instantiationService.createInstance(ContextScopedHistoryInputBox, container, this.contextViewService, { - placeholder: this.placeholder, - history: this.filters.filterHistory - })); - this._register(attachInputBoxStyler(this.filterInputBox, this.themeService)); - this.filterInputBox.value = this.filters.filterText; - this._register(this.filterInputBox.onDidChange(() => this.delayedFilterUpdate.trigger(() => this.onDidInputChange(this.filterInputBox!)))); - this._register(this.filters.onDidChange((event: IReplFiltersChangeEvent) => { - if (event.filterText) { - this.filterInputBox!.value = this.filters.filterText; - } - })); - this._register(DOM.addStandardDisposableListener(this.filterInputBox.inputElement, DOM.EventType.KEY_DOWN, (e: any) => this.onInputKeyDown(e))); - this._register(DOM.addStandardDisposableListener(container, DOM.EventType.KEY_DOWN, this.handleKeyboardEvent)); - this._register(DOM.addStandardDisposableListener(container, DOM.EventType.KEY_UP, this.handleKeyboardEvent)); - this._register(DOM.addStandardDisposableListener(this.filterInputBox.inputElement, DOM.EventType.CLICK, (e) => { - e.stopPropagation(); - e.preventDefault(); - })); - this._register(this.filters.onDidChange(e => this.onDidFiltersChange(e))); - } - - private onDidFiltersChange(e: IReplFiltersChangeEvent): void { - if (e.layout) { - this.updateClass(); - } - } - - private onDidInputChange(inputbox: HistoryInputBox) { - inputbox.addToHistory(); - this.filters.filterText = inputbox.value; - this.filters.filterHistory = inputbox.getHistory(); - } - - // Action toolbar is swallowing some keys for action items which should not be for an input box - private handleKeyboardEvent(event: StandardKeyboardEvent) { - if (event.equals(KeyCode.Space) - || event.equals(KeyCode.LeftArrow) - || event.equals(KeyCode.RightArrow) - || event.equals(KeyCode.Escape) - ) { - event.stopPropagation(); - } - } - - private onInputKeyDown(event: StandardKeyboardEvent) { - if (event.equals(KeyCode.Escape)) { - this.clearFilterText(); - event.stopPropagation(); - event.preventDefault(); - } - } - - private adjustInputBox(): void { - if (this.element && this.filterInputBox) { - this.filterInputBox.inputElement.style.paddingRight = DOM.hasClass(this.element, 'small') ? '25px' : '150px'; - } - } - - protected updateClass(): void { - if (this.element && this.container) { - this.element.className = this.class; - DOM.toggleClass(this.container, 'grow', DOM.hasClass(this.element, 'grow')); - this.adjustInputBox(); - } - } - - protected get class(): string { - if (this.filters.layout.width > 800) { - return 'panel-action-tree-filter grow'; - } else if (this.filters.layout.width < 600) { - return 'panel-action-tree-filter small'; - } else { - return 'panel-action-tree-filter'; - } - } -} From 62e2deb9311fb3f01d92c769663fc678a9272bcf Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 21 Aug 2020 12:00:16 -0700 Subject: [PATCH 437/736] common metadata editor. --- .../notebook/browser/diff/cellComponents.ts | 257 ++++++++++-------- 1 file changed, 139 insertions(+), 118 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index 2f6ca1f3c5c..c2ac2ebc30b 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -85,12 +85,20 @@ abstract class AbstractCellRenderer extends Disposable { metadataStatusHeight: number; metadataHeight: number; }; + protected _foldingIndicator!: HTMLElement; + protected _foldingState!: MetadataFoldingState; + protected _metadataEditorContainer?: HTMLElement; + protected _metadataEditorDisposeStore!: DisposableStore; + protected _metadataEditor?: CodeEditorWidget; constructor( readonly notebookEditor: INotebookTextDiffEditor, readonly cell: CellDiffViewModel, readonly templateData: CellDiffRenderTemplate, protected readonly instantiationService: IInstantiationService, + protected readonly modeService: IModeService, + protected readonly modelService: IModelService, + ) { super(); // init @@ -100,6 +108,8 @@ abstract class AbstractCellRenderer extends Disposable { metadataHeight: 0, metadataStatusHeight: 24 }; + this._metadataEditorDisposeStore = new DisposableStore(); + this._foldingState = MetadataFoldingState.Collapsed; this.initData(); this.buildBody(templateData.container); this._register(cell.onDidLayoutChange(e => this.onDidLayoutChange(e))); @@ -118,75 +128,6 @@ abstract class AbstractCellRenderer extends Disposable { this.buildMetadataBody(this._metadataInfoContainer); } - abstract initData(): void; - abstract styleContainer(container: HTMLElement): void; - abstract buildSourceEditor(sourceContainer: HTMLElement): void; - abstract buildMetadataHeader(metadataHeaderContainer: HTMLElement): void; - abstract buildMetadataBody(metadataBodyContainer: HTMLElement): void; - - abstract onDidLayoutChange(event: CellDiffViewModelLayoutChangeEvent): void; -} - -export class UnchangedCell extends AbstractCellRenderer { - private _editor!: CodeEditorWidget; - private _metadataEditor?: CodeEditorWidget; - private _foldingState!: MetadataFoldingState; - private _metadataEditorContainer?: HTMLElement; - private _foldingIndicator!: HTMLElement; - private _metadataEditorDisposeStore!: DisposableStore; - - constructor( - readonly notebookEditor: INotebookTextDiffEditor, - readonly cell: CellDiffViewModel, - readonly templateData: CellDiffRenderTemplate, - @IModeService private readonly modeService: IModeService, - @IModelService protected modelService: IModelService, - @IInstantiationService protected readonly instantiationService: IInstantiationService, - ) { - super(notebookEditor, cell, templateData, instantiationService); - } - - initData() { - this._foldingState = MetadataFoldingState.Collapsed; - this._metadataEditorDisposeStore = new DisposableStore(); - } - - styleContainer(container: HTMLElement) { - } - - buildSourceEditor(sourceContainer: HTMLElement) { - const editorContainer = DOM.append(sourceContainer, DOM.$('.editor-container')); - const originalCell = this.cell.original!; - const lineCount = originalCell.textBuffer.getLineCount(); - const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; - const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; - - this._editor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, { - ...fixedEditorOptions, - dimension: { - width: this.notebookEditor.getLayoutInfo().width - 20, - height: editorHeight - } - }, {}); - - this._register(this._editor.onDidContentSizeChange((e) => { - if (e.contentHeightChanged) { - this._layoutInfo.editorHeight = e.contentHeight; - this.layout({ editorHeight: true }); - } - })); - - originalCell.resolveTextModelRef().then(ref => { - this._register(ref); - - const textModel = ref.object.textEditorModel; - this._editor.setModel(textModel); - - this._layoutInfo.editorHeight = this._editor.getContentHeight(); - this.layout({ editorHeight: true }); - }); - } - buildMetadataHeader(metadataHeaderContainer: HTMLElement): void { this._foldingIndicator = DOM.append(metadataHeaderContainer, DOM.$('.metadata-folding-indicator')); this._updateFoldingIcon(); @@ -223,44 +164,6 @@ export class UnchangedCell extends AbstractCellRenderer { this.updateMetadataRendering(); } - buildMetadataBody(metadataBodyContainer: HTMLElement): void { - - } - - onDidLayoutChange(event: CellDiffViewModelLayoutChangeEvent): void { - if (event.outerWidth !== undefined) { - this.layout({ outerWidth: true }); - } - } - - layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean }) { - if (state.editorHeight || state.outerWidth) { - this._editor.layout({ - width: this.notebookEditor.getLayoutInfo().width - 20, - height: this._layoutInfo.editorHeight - }); - } - - if (state.metadataEditor || state.outerWidth) { - this._metadataEditor?.layout({ - width: this.notebookEditor.getLayoutInfo().width - 20, - height: this._layoutInfo.metadataHeight - }); - } - this.notebookEditor.layoutNotebookCell(this.cell, - this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight); - } - - private _updateFoldingIcon() { - if (this._foldingState === MetadataFoldingState.Collapsed) { - this._foldingIndicator.innerHTML = renderCodicons('$(chevron-right)'); - } else { - this._foldingIndicator.innerHTML = renderCodicons('$(chevron-down)'); - } - } - - buildMetadata(diffEditorContainer: HTMLElement) { - } updateMetadataRendering() { if (this._foldingState === MetadataFoldingState.Expanded) { @@ -310,6 +213,105 @@ export class UnchangedCell extends AbstractCellRenderer { this._updateFoldingIcon(); } + + private _updateFoldingIcon() { + if (this._foldingState === MetadataFoldingState.Collapsed) { + this._foldingIndicator.innerHTML = renderCodicons('$(chevron-right)'); + } else { + this._foldingIndicator.innerHTML = renderCodicons('$(chevron-down)'); + } + } + + abstract initData(): void; + abstract styleContainer(container: HTMLElement): void; + abstract buildSourceEditor(sourceContainer: HTMLElement): void; + abstract buildMetadataBody(metadataBodyContainer: HTMLElement): void; + + abstract onDidLayoutChange(event: CellDiffViewModelLayoutChangeEvent): void; + abstract layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean }): void; +} + +export class UnchangedCell extends AbstractCellRenderer { + private _editor!: CodeEditorWidget; + + constructor( + readonly notebookEditor: INotebookTextDiffEditor, + readonly cell: CellDiffViewModel, + readonly templateData: CellDiffRenderTemplate, + @IModeService readonly modeService: IModeService, + @IModelService protected modelService: IModelService, + @IInstantiationService protected readonly instantiationService: IInstantiationService, + ) { + super(notebookEditor, cell, templateData, instantiationService, modeService, modelService); + } + + initData() { + } + + styleContainer(container: HTMLElement) { + } + + buildSourceEditor(sourceContainer: HTMLElement) { + const editorContainer = DOM.append(sourceContainer, DOM.$('.editor-container')); + const originalCell = this.cell.original!; + const lineCount = originalCell.textBuffer.getLineCount(); + const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; + const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; + + this._editor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, { + ...fixedEditorOptions, + dimension: { + width: this.notebookEditor.getLayoutInfo().width - 20, + height: editorHeight + } + }, {}); + + this._register(this._editor.onDidContentSizeChange((e) => { + if (e.contentHeightChanged) { + this._layoutInfo.editorHeight = e.contentHeight; + this.layout({ editorHeight: true }); + } + })); + + originalCell.resolveTextModelRef().then(ref => { + this._register(ref); + + const textModel = ref.object.textEditorModel; + this._editor.setModel(textModel); + + this._layoutInfo.editorHeight = this._editor.getContentHeight(); + this.layout({ editorHeight: true }); + }); + } + + buildMetadataBody(metadataBodyContainer: HTMLElement): void { + + } + + onDidLayoutChange(event: CellDiffViewModelLayoutChangeEvent): void { + if (event.outerWidth !== undefined) { + this.layout({ outerWidth: true }); + } + } + + layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean }) { + if (state.editorHeight || state.outerWidth) { + this._editor.layout({ + width: this.notebookEditor.getLayoutInfo().width - 20, + height: this._layoutInfo.editorHeight + }); + } + + if (state.metadataEditor || state.outerWidth) { + this._metadataEditor?.layout({ + width: this.notebookEditor.getLayoutInfo().width - 20, + height: this._layoutInfo.metadataHeight + }); + } + + this.notebookEditor.layoutNotebookCell(this.cell, + this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight); + } } export class DeletedCell extends AbstractCellRenderer { @@ -319,9 +321,11 @@ export class DeletedCell extends AbstractCellRenderer { readonly notebookEditor: INotebookTextDiffEditor, readonly cell: CellDiffViewModel, readonly templateData: CellDiffRenderTemplate, + @IModeService readonly modeService: IModeService, + @IModelService readonly modelService: IModelService, @IInstantiationService protected readonly instantiationService: IInstantiationService, ) { - super(notebookEditor, cell, templateData, instantiationService); + super(notebookEditor, cell, templateData, instantiationService, modeService, modelService); } initData(): void { @@ -367,8 +371,6 @@ export class DeletedCell extends AbstractCellRenderer { }); } - buildMetadataHeader(metadataHeaderContainer: HTMLElement): void { - } buildMetadataBody(metadataBodyContainer: HTMLElement): void { } @@ -386,6 +388,13 @@ export class DeletedCell extends AbstractCellRenderer { }); } + if (state.metadataEditor || state.outerWidth) { + this._metadataEditor?.layout({ + width: this.notebookEditor.getLayoutInfo().width - 20, + height: this._layoutInfo.metadataHeight + }); + } + this._diagonalFill.style.height = `${this._layoutInfo.editorHeight}px`; this.notebookEditor.layoutNotebookCell(this.cell, @@ -401,8 +410,10 @@ export class InsertCell extends AbstractCellRenderer { readonly cell: CellDiffViewModel, readonly templateData: CellDiffRenderTemplate, @IInstantiationService protected readonly instantiationService: IInstantiationService, + @IModeService readonly modeService: IModeService, + @IModelService readonly modelService: IModelService, ) { - super(notebookEditor, cell, templateData, instantiationService); + super(notebookEditor, cell, templateData, instantiationService, modeService, modelService); } initData(): void { @@ -447,9 +458,6 @@ export class InsertCell extends AbstractCellRenderer { }); } - buildMetadataHeader(metadataHeaderContainer: HTMLElement): void { - } - buildMetadataBody(metadataBodyContainer: HTMLElement): void { } @@ -467,6 +475,13 @@ export class InsertCell extends AbstractCellRenderer { }); } + if (state.metadataEditor || state.outerWidth) { + this._metadataEditor?.layout({ + width: this.notebookEditor.getLayoutInfo().width - 20, + height: this._layoutInfo.metadataHeight + }); + } + this._diagonalFill.style.height = `${this._layoutInfo.editorHeight}px`; this.notebookEditor.layoutNotebookCell(this.cell, @@ -482,8 +497,10 @@ export class ModifiedCell extends AbstractCellRenderer { readonly cell: CellDiffViewModel, readonly templateData: CellDiffRenderTemplate, @IInstantiationService protected readonly instantiationService: IInstantiationService, + @IModeService readonly modeService: IModeService, + @IModelService readonly modelService: IModelService, ) { - super(notebookEditor, cell, templateData, instantiationService); + super(notebookEditor, cell, templateData, instantiationService, modeService, modelService); } initData(): void { @@ -542,9 +559,6 @@ export class ModifiedCell extends AbstractCellRenderer { } - buildMetadataHeader(metadataHeaderContainer: HTMLElement): void { - } - buildMetadataBody(metadataBodyContainer: HTMLElement): void { } @@ -560,6 +574,13 @@ export class ModifiedCell extends AbstractCellRenderer { this._editor!.layout(); } + if (state.metadataEditor || state.outerWidth) { + this._metadataEditor?.layout({ + width: this.notebookEditor.getLayoutInfo().width - 20, + height: this._layoutInfo.metadataHeight + }); + } + this.notebookEditor.layoutNotebookCell(this.cell, this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight); From b2107a2d872a2d16ad5837917b54f9a342979d99 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 21 Aug 2020 12:09:49 -0700 Subject: [PATCH 438/736] Extract MainThreadWebviewSerializers to own file Fixes #105127 This time make sure to avoid cycles too --- .../api/browser/mainThreadWebview.ts | 94 ++++------------ .../browser/mainThreadWebviewSerializer.ts | 102 ++++++++++++++++++ 2 files changed, 123 insertions(+), 73 deletions(-) create mode 100644 src/vs/workbench/api/browser/mainThreadWebviewSerializer.ts diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index 8d2d1a3f72e..2dca9cca41e 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -25,6 +25,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IProductService } from 'vs/platform/product/common/productService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo'; +import { MainThreadWebviewSerializers } from 'vs/workbench/api/browser/mainThreadWebviewSerializer'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; import { IEditorInput, IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor'; @@ -104,8 +105,6 @@ const enum ModelType { Text, } -const webviewPanelViewType = new WebviewViewTypeTransformer('mainThreadWebview-'); - @extHostNamedCustomer(extHostProtocol.MainContext.MainThreadWebviews) export class MainThreadWebviews extends Disposable implements extHostProtocol.MainThreadWebviewsShape { @@ -117,13 +116,13 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma 'vscode-insider', ]); + public readonly webviewPanelViewType = new WebviewViewTypeTransformer('mainThreadWebview-'); + private readonly _proxy: extHostProtocol.ExtHostWebviewsShape; - private readonly _proxySerializer: extHostProtocol.ExtHostWebviewSerializerShape; private readonly _proxyViews: extHostProtocol.ExtHostWebviewViewsShape; private readonly _proxyCustomEditors: extHostProtocol.ExtHostCustomEditorsShape; private readonly _webviewInputs = new WebviewInputStore(); - private readonly _revivers = new Map(); private readonly _webviewViewProviders = new Map(); private readonly _webviewViews = new Map(); @@ -131,6 +130,8 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma private readonly _editorProviders = new Map(); private readonly _webviewFromDiffEditorHandles = new Set(); + private readonly serializers: MainThreadWebviewSerializers; + constructor( context: extHostProtocol.IExtHostContext, @IExtensionService extensionService: IExtensionService, @@ -149,8 +150,9 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma ) { super(); + this.serializers = new MainThreadWebviewSerializers(this, context, extensionService, _editorGroupService, _webviewWorkbenchService); + this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviews); - this._proxySerializer = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewSerializer); this._proxyViews = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewViews); this._proxyCustomEditors = context.getProxy(extHostProtocol.ExtHostContext.ExtHostCustomEditors); @@ -167,24 +169,6 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma this.updateWebviewViewStates(this._editorService.activeEditor); })); - // This reviver's only job is to activate extensions. - // This should trigger the real reviver to be registered from the extension host side. - this._register(_webviewWorkbenchService.registerResolver({ - canResolve: (webview: WebviewInput) => { - if (webview instanceof CustomEditorInput) { - extensionService.activateByEvent(`onCustomEditor:${webview.viewType}`); - return false; - } - - const viewType = webviewPanelViewType.toExternal(webview.viewType); - if (typeof viewType === 'string') { - extensionService.activateByEvent(`onWebviewPanel:${viewType}`); - } - return false; - }, - resolveWebview: () => { throw new Error('not implemented'); } - })); - workingCopyFileService.registerWorkingCopyProvider((editorResource) => { const matchedWorkingCopies: IWorkingCopy[] = []; @@ -209,6 +193,11 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma this._editorProviders.clear(); } + public addWebviewInput(handle: extHostProtocol.WebviewPanelHandle, input: WebviewInput): void { + this._webviewInputs.add(handle, input); + this.hookupWebviewEventDelegate(handle, input.webview); + } + public $createWebviewPanel( extensionData: extHostProtocol.WebviewExtensionDescription, handle: extHostProtocol.WebviewPanelHandle, @@ -224,7 +213,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma } const extension = reviveWebviewExtension(extensionData); - const webview = this._webviewWorkbenchService.createWebview(handle, webviewPanelViewType.fromExternal(viewType), title, mainThreadShowOptions, reviveWebviewOptions(options), extension); + const webview = this._webviewWorkbenchService.createWebview(handle, this.webviewPanelViewType.fromExternal(viewType), title, mainThreadShowOptions, reviveWebviewOptions(options), extension); this.hookupWebviewEventDelegate(handle, webview.webview); this._webviewInputs.add(handle, webview); @@ -288,53 +277,12 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma return true; } - public $registerSerializer(viewType: string): void { - if (this._revivers.has(viewType)) { - throw new Error(`Reviver for ${viewType} already registered`); - } - - this._revivers.set(viewType, this._webviewWorkbenchService.registerResolver({ - canResolve: (webviewInput) => { - return webviewInput.viewType === webviewPanelViewType.fromExternal(viewType); - }, - resolveWebview: async (webviewInput): Promise => { - const viewType = webviewPanelViewType.toExternal(webviewInput.viewType); - if (!viewType) { - webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(webviewInput.viewType); - return; - } - - const handle = webviewInput.id; - this._webviewInputs.add(handle, webviewInput); - this.hookupWebviewEventDelegate(handle, webviewInput.webview); - - let state = undefined; - if (webviewInput.webview.state) { - try { - state = JSON.parse(webviewInput.webview.state); - } catch (e) { - console.error('Could not load webview state', e, webviewInput.webview.state); - } - } - - try { - await this._proxySerializer.$deserializeWebviewPanel(handle, viewType, webviewInput.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options); - } catch (error) { - onUnexpectedError(error); - webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(viewType); - } - } - })); + $registerSerializer(viewType: string): void { + this.serializers.$registerSerializer(viewType); } - public $unregisterSerializer(viewType: string): void { - const reviver = this._revivers.get(viewType); - if (!reviver) { - throw new Error(`No reviver for ${viewType} registered`); - } - - reviver.dispose(); - this._revivers.delete(viewType); + $unregisterSerializer(viewType: string): void { + this.serializers.$unregisterSerializer(viewType); } public $registerWebviewViewProvider(viewType: string, options?: { retainContextWhenHidden?: boolean }): void { @@ -374,7 +322,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma await this._proxyViews.$resolveWebviewView(handle, viewType, state, cancellation); } catch (error) { onUnexpectedError(error); - webviewView.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(viewType); + webviewView.webview.html = this.getWebviewResolvedFailedContent(viewType); } } }); @@ -436,7 +384,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma modelRef = await this.getOrCreateCustomEditorModel(modelType, resource, viewType, { backupId: webviewInput.backupId }, cancellation); } catch (error) { onUnexpectedError(error); - webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(viewType); + webviewInput.webview.html = this.getWebviewResolvedFailedContent(viewType); return; } @@ -473,7 +421,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma await this._proxyCustomEditors.$resolveWebviewEditor(resource, handle, viewType, webviewInput.getTitle(), editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options, cancellation); } catch (error) { onUnexpectedError(error); - webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(viewType); + webviewInput.webview.html = this.getWebviewResolvedFailedContent(viewType); modelRef.dispose(); return; } @@ -657,7 +605,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma return model; } - private static getWebviewResolvedFailedContent(viewType: string) { + public getWebviewResolvedFailedContent(viewType: string) { return ` diff --git a/src/vs/workbench/api/browser/mainThreadWebviewSerializer.ts b/src/vs/workbench/api/browser/mainThreadWebviewSerializer.ts new file mode 100644 index 00000000000..f72f472d6ce --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadWebviewSerializer.ts @@ -0,0 +1,102 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { onUnexpectedError } from 'vs/base/common/errors'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import type { MainThreadWebviews } from 'vs/workbench/api/browser/mainThreadWebview'; +import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; +import { editorGroupToViewColumn } from 'vs/workbench/api/common/shared/editor'; +import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; +import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; +import { IWebviewWorkbenchService } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; + +export class MainThreadWebviewSerializers extends Disposable { + + private readonly _proxy: extHostProtocol.ExtHostWebviewSerializerShape; + + private readonly _revivers = new Map(); + + constructor( + private readonly mainThreadWebviews: MainThreadWebviews, + context: extHostProtocol.IExtHostContext, + @IExtensionService extensionService: IExtensionService, + @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, + @IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService, + ) { + super(); + + this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewSerializer); + + // This reviver's only job is to activate extensions. + // This should trigger the real reviver to be registered from the extension host side. + this._register(_webviewWorkbenchService.registerResolver({ + canResolve: (webview: WebviewInput) => { + if (webview instanceof CustomEditorInput) { + extensionService.activateByEvent(`onCustomEditor:${webview.viewType}`); + return false; + } + + const viewType = this.mainThreadWebviews.webviewPanelViewType.toExternal(webview.viewType); + if (typeof viewType === 'string') { + extensionService.activateByEvent(`onWebviewPanel:${viewType}`); + } + return false; + }, + resolveWebview: () => { throw new Error('not implemented'); } + })); + } + + public $registerSerializer(viewType: string): void { + if (this._revivers.has(viewType)) { + throw new Error(`Reviver for ${viewType} already registered`); + } + + this._revivers.set(viewType, this._webviewWorkbenchService.registerResolver({ + canResolve: (webviewInput) => { + return webviewInput.viewType === this.mainThreadWebviews.webviewPanelViewType.fromExternal(viewType); + }, + resolveWebview: async (webviewInput): Promise => { + const viewType = this.mainThreadWebviews.webviewPanelViewType.toExternal(webviewInput.viewType); + if (!viewType) { + webviewInput.webview.html = this.mainThreadWebviews.getWebviewResolvedFailedContent(webviewInput.viewType); + return; + } + + + const handle = webviewInput.id; + + this.mainThreadWebviews.addWebviewInput(handle, webviewInput); + + let state = undefined; + if (webviewInput.webview.state) { + try { + state = JSON.parse(webviewInput.webview.state); + } catch (e) { + console.error('Could not load webview state', e, webviewInput.webview.state); + } + } + + try { + await this._proxy.$deserializeWebviewPanel(handle, viewType, webviewInput.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options); + } catch (error) { + onUnexpectedError(error); + webviewInput.webview.html = this.mainThreadWebviews.getWebviewResolvedFailedContent(viewType); + } + } + })); + } + + public $unregisterSerializer(viewType: string): void { + const reviver = this._revivers.get(viewType); + if (!reviver) { + throw new Error(`No reviver for ${viewType} registered`); + } + + reviver.dispose(); + this._revivers.delete(viewType); + } +} From d192ba880687605da0ab23df611e926774b25755 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 21 Aug 2020 12:22:00 -0700 Subject: [PATCH 439/736] Extract main thread custom editors to own file --- .../api/browser/mainThreadCustomEditors.ts | 599 ++++++++++++++++++ .../api/browser/mainThreadWebview.ts | 566 +---------------- 2 files changed, 613 insertions(+), 552 deletions(-) create mode 100644 src/vs/workbench/api/browser/mainThreadCustomEditors.ts diff --git a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts new file mode 100644 index 00000000000..8e26e31cd18 --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts @@ -0,0 +1,599 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable, DisposableStore, IDisposable, IReference } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; +import { basename } from 'vs/base/common/path'; +import { isEqual, isEqualOrParent, toLocalResource } from 'vs/base/common/resources'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import * as modes from 'vs/editor/common/modes'; +import { localize } from 'vs/nls'; +import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo'; +import type { MainThreadWebviews } from 'vs/workbench/api/browser/mainThreadWebview'; +import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; +import { editorGroupToViewColumn } from 'vs/workbench/api/common/shared/editor'; +import { IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor'; +import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; +import { CustomDocumentBackupData } from 'vs/workbench/contrib/customEditor/browser/customEditorInputFactory'; +import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; +import { CustomTextEditorModel } from 'vs/workbench/contrib/customEditor/common/customTextEditorModel'; +import { WebviewExtensionDescription } from 'vs/workbench/contrib/webview/browser/webview'; +import { IWebviewWorkbenchService } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; +import { IWorkingCopy, IWorkingCopyBackup, IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; + +export const enum CustomEditorModelType { + Custom, + Text, +} + +export class MainThreadCustomEditors extends Disposable { + + private readonly _proxyCustomEditors: extHostProtocol.ExtHostCustomEditorsShape; + + private readonly _editorProviders = new Map(); + + constructor( + private readonly mainThreadWebviews: MainThreadWebviews, + context: extHostProtocol.IExtHostContext, + @IWorkingCopyService workingCopyService: IWorkingCopyService, + @IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService, + @ICustomEditorService private readonly _customEditorService: ICustomEditorService, + @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, + @IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IBackupFileService private readonly _backupService: IBackupFileService, + ) { + super(); + + this._proxyCustomEditors = context.getProxy(extHostProtocol.ExtHostContext.ExtHostCustomEditors); + + workingCopyFileService.registerWorkingCopyProvider((editorResource) => { + const matchedWorkingCopies: IWorkingCopy[] = []; + + for (const workingCopy of workingCopyService.workingCopies) { + if (workingCopy instanceof MainThreadCustomEditorModel) { + if (isEqualOrParent(editorResource, workingCopy.editorResource)) { + matchedWorkingCopies.push(workingCopy); + } + } + } + return matchedWorkingCopies; + }); + } + + dispose() { + super.dispose(); + + for (const disposable of this._editorProviders.values()) { + disposable.dispose(); + } + + this._editorProviders.clear(); + } + + public registerEditorProvider( + modelType: CustomEditorModelType, + extension: WebviewExtensionDescription, + viewType: string, + options: modes.IWebviewPanelOptions, + capabilities: extHostProtocol.CustomTextEditorCapabilities, + supportsMultipleEditorsPerDocument: boolean, + ): void { + if (this._editorProviders.has(viewType)) { + throw new Error(`Provider for ${viewType} already registered`); + } + + + const disposables = new DisposableStore(); + + disposables.add(this._customEditorService.registerCustomEditorCapabilities(viewType, { + supportsMultipleEditorsPerDocument + })); + + disposables.add(this._webviewWorkbenchService.registerResolver({ + canResolve: (webviewInput) => { + return webviewInput instanceof CustomEditorInput && webviewInput.viewType === viewType; + }, + resolveWebview: async (webviewInput: CustomEditorInput, cancellation: CancellationToken) => { + const handle = webviewInput.id; + const resource = webviewInput.resource; + + this.mainThreadWebviews.addWebviewInput(handle, webviewInput); + webviewInput.webview.options = options; + webviewInput.webview.extension = extension; + + let modelRef: IReference; + try { + modelRef = await this.getOrCreateCustomEditorModel(modelType, resource, viewType, { backupId: webviewInput.backupId }, cancellation); + } catch (error) { + onUnexpectedError(error); + webviewInput.webview.html = this.mainThreadWebviews.getWebviewResolvedFailedContent(viewType); + return; + } + + if (cancellation.isCancellationRequested) { + modelRef.dispose(); + return; + } + + webviewInput.webview.onDispose(() => { + // If the model is still dirty, make sure we have time to save it + if (modelRef.object.isDirty()) { + const sub = modelRef.object.onDidChangeDirty(() => { + if (!modelRef.object.isDirty()) { + sub.dispose(); + modelRef.dispose(); + } + }); + return; + } + + modelRef.dispose(); + }); + + if (capabilities.supportsMove) { + webviewInput.onMove(async (newResource: URI) => { + const oldModel = modelRef; + modelRef = await this.getOrCreateCustomEditorModel(modelType, newResource, viewType, {}, CancellationToken.None); + this._proxyCustomEditors.$onMoveCustomEditor(handle, newResource, viewType); + oldModel.dispose(); + }); + } + + try { + await this._proxyCustomEditors.$resolveWebviewEditor(resource, handle, viewType, webviewInput.getTitle(), editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options, cancellation); + } catch (error) { + onUnexpectedError(error); + webviewInput.webview.html = this.mainThreadWebviews.getWebviewResolvedFailedContent(viewType); + modelRef.dispose(); + return; + } + } + })); + + this._editorProviders.set(viewType, disposables); + } + + public $unregisterEditorProvider(viewType: string): void { + const provider = this._editorProviders.get(viewType); + if (!provider) { + throw new Error(`No provider for ${viewType} registered`); + } + + provider.dispose(); + this._editorProviders.delete(viewType); + + this._customEditorService.models.disposeAllModelsForView(viewType); + } + + private async getOrCreateCustomEditorModel( + modelType: CustomEditorModelType, + resource: URI, + viewType: string, + options: { backupId?: string }, + cancellation: CancellationToken, + ): Promise> { + const existingModel = this._customEditorService.models.tryRetain(resource, viewType); + if (existingModel) { + return existingModel; + } + + switch (modelType) { + case CustomEditorModelType.Text: + { + const model = CustomTextEditorModel.create(this._instantiationService, viewType, resource); + return this._customEditorService.models.add(resource, viewType, model); + } + case CustomEditorModelType.Custom: + { + const model = MainThreadCustomEditorModel.create(this._instantiationService, this._proxyCustomEditors, viewType, resource, options, () => { + return Array.from(this.mainThreadWebviews.webviewInputs) + .filter(editor => editor instanceof CustomEditorInput && isEqual(editor.resource, resource)) as CustomEditorInput[]; + }, cancellation, this._backupService); + return this._customEditorService.models.add(resource, viewType, model); + } + } + } + + public async $onDidEdit(resourceComponents: UriComponents, viewType: string, editId: number, label: string | undefined): Promise { + const model = await this.getCustomEditorModel(resourceComponents, viewType); + model.pushEdit(editId, label); + } + + public async $onContentChange(resourceComponents: UriComponents, viewType: string): Promise { + const model = await this.getCustomEditorModel(resourceComponents, viewType); + model.changeContent(); + } + + private async getCustomEditorModel(resourceComponents: UriComponents, viewType: string) { + const resource = URI.revive(resourceComponents); + const model = await this._customEditorService.models.get(resource, viewType); + if (!model || !(model instanceof MainThreadCustomEditorModel)) { + throw new Error('Could not find model for webview editor'); + } + return model; + } +} + +namespace HotExitState { + export const enum Type { + Allowed, + NotAllowed, + Pending, + } + + export const Allowed = Object.freeze({ type: Type.Allowed } as const); + export const NotAllowed = Object.freeze({ type: Type.NotAllowed } as const); + + export class Pending { + readonly type = Type.Pending; + + constructor( + public readonly operation: CancelablePromise, + ) { } + } + + export type State = typeof Allowed | typeof NotAllowed | Pending; +} + + +class MainThreadCustomEditorModel extends Disposable implements ICustomEditorModel, IWorkingCopy { + + private _fromBackup: boolean = false; + private _hotExitState: HotExitState.State = HotExitState.Allowed; + private _backupId: string | undefined; + + private _currentEditIndex: number = -1; + private _savePoint: number = -1; + private readonly _edits: Array = []; + private _isDirtyFromContentChange = false; + + private _ongoingSave?: CancelablePromise; + + public static async create( + instantiationService: IInstantiationService, + proxy: extHostProtocol.ExtHostCustomEditorsShape, + viewType: string, + resource: URI, + options: { backupId?: string }, + getEditors: () => CustomEditorInput[], + cancellation: CancellationToken, + _backupFileService: IBackupFileService, + ) { + const { editable } = await proxy.$createCustomDocument(resource, viewType, options.backupId, cancellation); + return instantiationService.createInstance(MainThreadCustomEditorModel, proxy, viewType, resource, !!options.backupId, editable, getEditors); + } + + constructor( + private readonly _proxy: extHostProtocol.ExtHostCustomEditorsShape, + private readonly _viewType: string, + private readonly _editorResource: URI, + fromBackup: boolean, + private readonly _editable: boolean, + private readonly _getEditors: () => CustomEditorInput[], + @IFileDialogService private readonly _fileDialogService: IFileDialogService, + @IFileService private readonly _fileService: IFileService, + @ILabelService private readonly _labelService: ILabelService, + @IUndoRedoService private readonly _undoService: IUndoRedoService, + @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, + @IWorkingCopyService workingCopyService: IWorkingCopyService, + ) { + super(); + + this._fromBackup = fromBackup; + + if (_editable) { + this._register(workingCopyService.registerWorkingCopy(this)); + } + } + + get editorResource() { + return this._editorResource; + } + + dispose() { + if (this._editable) { + this._undoService.removeElements(this._editorResource); + } + this._proxy.$disposeCustomDocument(this._editorResource, this._viewType); + super.dispose(); + } + + //#region IWorkingCopy + + public get resource() { + // Make sure each custom editor has a unique resource for backup and edits + return MainThreadCustomEditorModel.toWorkingCopyResource(this._viewType, this._editorResource); + } + + private static toWorkingCopyResource(viewType: string, resource: URI) { + return URI.from({ + scheme: Schemas.vscodeCustomEditor, + authority: viewType, + path: resource.path, + query: JSON.stringify(resource.toJSON()), + }); + } + + public get name() { + return basename(this._labelService.getUriLabel(this._editorResource)); + } + + public get capabilities(): WorkingCopyCapabilities { + return 0; + } + + public isDirty(): boolean { + if (this._isDirtyFromContentChange) { + return true; + } + if (this._edits.length > 0) { + return this._savePoint !== this._currentEditIndex; + } + return this._fromBackup; + } + + private readonly _onDidChangeDirty: Emitter = this._register(new Emitter()); + readonly onDidChangeDirty: Event = this._onDidChangeDirty.event; + + private readonly _onDidChangeContent: Emitter = this._register(new Emitter()); + readonly onDidChangeContent: Event = this._onDidChangeContent.event; + + //#endregion + + public isReadonly() { + return !this._editable; + } + + public get viewType() { + return this._viewType; + } + + public get backupId() { + return this._backupId; + } + + public pushEdit(editId: number, label: string | undefined) { + if (!this._editable) { + throw new Error('Document is not editable'); + } + + this.change(() => { + this.spliceEdits(editId); + this._currentEditIndex = this._edits.length - 1; + }); + + this._undoService.pushElement({ + type: UndoRedoElementType.Resource, + resource: this._editorResource, + label: label ?? localize('defaultEditLabel', "Edit"), + undo: () => this.undo(), + redo: () => this.redo(), + }); + } + + public changeContent() { + this.change(() => { + this._isDirtyFromContentChange = true; + }); + } + + private async undo(): Promise { + if (!this._editable) { + return; + } + + if (this._currentEditIndex < 0) { + // nothing to undo + return; + } + + const undoneEdit = this._edits[this._currentEditIndex]; + this.change(() => { + --this._currentEditIndex; + }); + await this._proxy.$undo(this._editorResource, this.viewType, undoneEdit, this.isDirty()); + } + + private async redo(): Promise { + if (!this._editable) { + return; + } + + if (this._currentEditIndex >= this._edits.length - 1) { + // nothing to redo + return; + } + + const redoneEdit = this._edits[this._currentEditIndex + 1]; + this.change(() => { + ++this._currentEditIndex; + }); + await this._proxy.$redo(this._editorResource, this.viewType, redoneEdit, this.isDirty()); + } + + private spliceEdits(editToInsert?: number) { + const start = this._currentEditIndex + 1; + const toRemove = this._edits.length - this._currentEditIndex; + + const removedEdits = typeof editToInsert === 'number' + ? this._edits.splice(start, toRemove, editToInsert) + : this._edits.splice(start, toRemove); + + if (removedEdits.length) { + this._proxy.$disposeEdits(this._editorResource, this._viewType, removedEdits); + } + } + + private change(makeEdit: () => void): void { + const wasDirty = this.isDirty(); + makeEdit(); + this._onDidChangeContent.fire(); + + if (this.isDirty() !== wasDirty) { + this._onDidChangeDirty.fire(); + } + } + + public async revert(_options?: IRevertOptions) { + if (!this._editable) { + return; + } + + if (this._currentEditIndex === this._savePoint && !this._isDirtyFromContentChange && !this._fromBackup) { + return; + } + + this._proxy.$revert(this._editorResource, this.viewType, CancellationToken.None); + this.change(() => { + this._isDirtyFromContentChange = false; + this._fromBackup = false; + this._currentEditIndex = this._savePoint; + this.spliceEdits(); + }); + } + + public async save(options?: ISaveOptions): Promise { + return !!await this.saveCustomEditor(options); + } + + public async saveCustomEditor(options?: ISaveOptions): Promise { + if (!this._editable) { + return undefined; + } + + if (this._editorResource.scheme === Schemas.untitled) { + const targetUri = await this.suggestUntitledSavePath(options); + if (!targetUri) { + return undefined; + } + + await this.saveCustomEditorAs(this._editorResource, targetUri, options); + return targetUri; + } + + const savePromise = createCancelablePromise(token => this._proxy.$onSave(this._editorResource, this.viewType, token)); + this._ongoingSave?.cancel(); + this._ongoingSave = savePromise; + + this.change(() => { + this._isDirtyFromContentChange = false; + this._savePoint = this._currentEditIndex; + this._fromBackup = false; + }); + + try { + await savePromise; + } finally { + if (this._ongoingSave === savePromise) { + this._ongoingSave = undefined; + } + } + return this._editorResource; + } + + private suggestUntitledSavePath(options: ISaveOptions | undefined): Promise { + if (this._editorResource.scheme !== Schemas.untitled) { + throw new Error('Resource is not untitled'); + } + + const remoteAuthority = this._environmentService.configuration.remoteAuthority; + const localResrouce = toLocalResource(this._editorResource, remoteAuthority); + + + return this._fileDialogService.pickFileToSave(localResrouce, options?.availableFileSystems); + } + + public async saveCustomEditorAs(resource: URI, targetResource: URI, _options?: ISaveOptions): Promise { + if (this._editable) { + // TODO: handle cancellation + await createCancelablePromise(token => this._proxy.$onSaveAs(this._editorResource, this.viewType, targetResource, token)); + this.change(() => { + this._savePoint = this._currentEditIndex; + }); + return true; + } else { + // Since the editor is readonly, just copy the file over + await this._fileService.copy(resource, targetResource, false /* overwrite */); + return true; + } + } + + public async backup(): Promise { + const editors = this._getEditors(); + if (!editors.length) { + throw new Error('No editors found for resource, cannot back up'); + } + const primaryEditor = editors[0]; + + const backupData: IWorkingCopyBackup = { + meta: { + viewType: this.viewType, + editorResource: this._editorResource, + backupId: '', + extension: primaryEditor.extension ? { + id: primaryEditor.extension.id.value, + location: primaryEditor.extension.location, + } : undefined, + webview: { + id: primaryEditor.id, + options: primaryEditor.webview.options, + state: primaryEditor.webview.state, + } + } + }; + + if (!this._editable) { + return backupData; + } + + if (this._hotExitState.type === HotExitState.Type.Pending) { + this._hotExitState.operation.cancel(); + } + + const pendingState = new HotExitState.Pending( + createCancelablePromise(token => + this._proxy.$backup(this._editorResource.toJSON(), this.viewType, token))); + this._hotExitState = pendingState; + + try { + const backupId = await pendingState.operation; + // Make sure state has not changed in the meantime + if (this._hotExitState === pendingState) { + this._hotExitState = HotExitState.Allowed; + backupData.meta!.backupId = backupId; + this._backupId = backupId; + } + } catch (e) { + if (isPromiseCanceledError(e)) { + // This is expected + throw e; + } + + // Otherwise it could be a real error. Make sure state has not changed in the meantime. + if (this._hotExitState === pendingState) { + this._hotExitState = HotExitState.NotAllowed; + } + } + + if (this._hotExitState === HotExitState.Allowed) { + return backupData; + } + + throw new Error('Cannot back up in this state'); + } +} diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index 2dca9cca41e..f2a43a2160a 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -3,48 +3,32 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors'; -import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, DisposableStore, dispose, IDisposable, IReference } from 'vs/base/common/lifecycle'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; -import { basename } from 'vs/base/common/path'; import { isWeb } from 'vs/base/common/platform'; -import { isEqual, isEqualOrParent, toLocalResource } from 'vs/base/common/resources'; import { escape } from 'vs/base/common/strings'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as modes from 'vs/editor/common/modes'; import { localize } from 'vs/nls'; -import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ILabelService } from 'vs/platform/label/common/label'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IProductService } from 'vs/platform/product/common/productService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo'; +import { MainThreadCustomEditors, CustomEditorModelType } from 'vs/workbench/api/browser/mainThreadCustomEditors'; import { MainThreadWebviewSerializers } from 'vs/workbench/api/browser/mainThreadWebviewSerializer'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; -import { IEditorInput, IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor'; +import { IEditorInput } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; -import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; -import { CustomDocumentBackupData } from 'vs/workbench/contrib/customEditor/browser/customEditorInputFactory'; -import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; -import { CustomTextEditorModel } from 'vs/workbench/contrib/customEditor/common/customTextEditorModel'; import { Webview, WebviewExtensionDescription, WebviewIcons, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; import { ICreateWebViewShowOptions, IWebviewWorkbenchService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; import { IWebviewViewService, WebviewView } from 'vs/workbench/contrib/webviewView/browser/webviewViewService'; -import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; -import { IWorkingCopy, IWorkingCopyBackup, IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { extHostNamedCustomer } from '../common/extHostCustomers'; /** @@ -100,11 +84,6 @@ class WebviewViewTypeTransformer { } } -const enum ModelType { - Custom, - Text, -} - @extHostNamedCustomer(extHostProtocol.MainContext.MainThreadWebviews) export class MainThreadWebviews extends Disposable implements extHostProtocol.MainThreadWebviewsShape { @@ -120,7 +99,6 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma private readonly _proxy: extHostProtocol.ExtHostWebviewsShape; private readonly _proxyViews: extHostProtocol.ExtHostWebviewViewsShape; - private readonly _proxyCustomEditors: extHostProtocol.ExtHostCustomEditorsShape; private readonly _webviewInputs = new WebviewInputStore(); @@ -131,13 +109,10 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma private readonly _webviewFromDiffEditorHandles = new Set(); private readonly serializers: MainThreadWebviewSerializers; + private readonly customEditors: MainThreadCustomEditors; constructor( context: extHostProtocol.IExtHostContext, - @IExtensionService extensionService: IExtensionService, - @IWorkingCopyService workingCopyService: IWorkingCopyService, - @IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService, - @ICustomEditorService private readonly _customEditorService: ICustomEditorService, @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, @IEditorService private readonly _editorService: IEditorService, @IOpenerService private readonly _openerService: IOpenerService, @@ -145,16 +120,15 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma @ITelemetryService private readonly _telemetryService: ITelemetryService, @IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IBackupFileService private readonly _backupService: IBackupFileService, @IWebviewViewService private readonly _webviewViewService: IWebviewViewService, ) { super(); - this.serializers = new MainThreadWebviewSerializers(this, context, extensionService, _editorGroupService, _webviewWorkbenchService); + this.serializers = this._instantiationService.createInstance(MainThreadWebviewSerializers, this, context); + this.customEditors = this._instantiationService.createInstance(MainThreadCustomEditors, this, context); this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviews); this._proxyViews = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewViews); - this._proxyCustomEditors = context.getProxy(extHostProtocol.ExtHostContext.ExtHostCustomEditors); this._register(_editorService.onDidActiveEditorChange(() => { const activeInput = this._editorService.activeEditor; @@ -169,19 +143,6 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma this.updateWebviewViewStates(this._editorService.activeEditor); })); - workingCopyFileService.registerWorkingCopyProvider((editorResource) => { - const matchedWorkingCopies: IWorkingCopy[] = []; - - for (const workingCopy of workingCopyService.workingCopies) { - if (workingCopy instanceof MainThreadCustomEditorModel) { - if (isEqualOrParent(editorResource, workingCopy.editorResource)) { - matchedWorkingCopies.push(workingCopy); - } - } - } - return matchedWorkingCopies; - - }); } dispose() { @@ -193,6 +154,8 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma this._editorProviders.clear(); } + public get webviewInputs(): Iterable { return this._webviewInputs; } + public addWebviewInput(handle: extHostProtocol.WebviewPanelHandle, input: WebviewInput): void { this._webviewInputs.add(handle, input); this.hookupWebviewEventDelegate(handle, input.webview); @@ -339,147 +302,23 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma } public $registerTextEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: extHostProtocol.CustomTextEditorCapabilities): void { - this.registerEditorProvider(ModelType.Text, extensionData, viewType, options, capabilities, true); + this.customEditors.registerEditorProvider(CustomEditorModelType.Text, reviveWebviewExtension(extensionData), viewType, options, capabilities, true); } public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void { - this.registerEditorProvider(ModelType.Custom, extensionData, viewType, options, {}, supportsMultipleEditorsPerDocument); - } - - private registerEditorProvider( - modelType: ModelType, - extensionData: extHostProtocol.WebviewExtensionDescription, - viewType: string, - options: modes.IWebviewPanelOptions, - capabilities: extHostProtocol.CustomTextEditorCapabilities, - supportsMultipleEditorsPerDocument: boolean, - ): void { - if (this._editorProviders.has(viewType)) { - throw new Error(`Provider for ${viewType} already registered`); - } - - const extension = reviveWebviewExtension(extensionData); - - const disposables = new DisposableStore(); - - disposables.add(this._customEditorService.registerCustomEditorCapabilities(viewType, { - supportsMultipleEditorsPerDocument - })); - - disposables.add(this._webviewWorkbenchService.registerResolver({ - canResolve: (webviewInput) => { - return webviewInput instanceof CustomEditorInput && webviewInput.viewType === viewType; - }, - resolveWebview: async (webviewInput: CustomEditorInput, cancellation: CancellationToken) => { - const handle = webviewInput.id; - const resource = webviewInput.resource; - - this._webviewInputs.add(handle, webviewInput); - this.hookupWebviewEventDelegate(handle, webviewInput.webview); - webviewInput.webview.options = options; - webviewInput.webview.extension = extension; - - let modelRef: IReference; - try { - modelRef = await this.getOrCreateCustomEditorModel(modelType, resource, viewType, { backupId: webviewInput.backupId }, cancellation); - } catch (error) { - onUnexpectedError(error); - webviewInput.webview.html = this.getWebviewResolvedFailedContent(viewType); - return; - } - - if (cancellation.isCancellationRequested) { - modelRef.dispose(); - return; - } - - webviewInput.webview.onDispose(() => { - // If the model is still dirty, make sure we have time to save it - if (modelRef.object.isDirty()) { - const sub = modelRef.object.onDidChangeDirty(() => { - if (!modelRef.object.isDirty()) { - sub.dispose(); - modelRef.dispose(); - } - }); - return; - } - - modelRef.dispose(); - }); - - if (capabilities.supportsMove) { - webviewInput.onMove(async (newResource: URI) => { - const oldModel = modelRef; - modelRef = await this.getOrCreateCustomEditorModel(modelType, newResource, viewType, {}, CancellationToken.None); - this._proxyCustomEditors.$onMoveCustomEditor(handle, newResource, viewType); - oldModel.dispose(); - }); - } - - try { - await this._proxyCustomEditors.$resolveWebviewEditor(resource, handle, viewType, webviewInput.getTitle(), editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options, cancellation); - } catch (error) { - onUnexpectedError(error); - webviewInput.webview.html = this.getWebviewResolvedFailedContent(viewType); - modelRef.dispose(); - return; - } - } - })); - - this._editorProviders.set(viewType, disposables); + this.customEditors.registerEditorProvider(CustomEditorModelType.Custom, reviveWebviewExtension(extensionData), viewType, options, {}, supportsMultipleEditorsPerDocument); } public $unregisterEditorProvider(viewType: string): void { - const provider = this._editorProviders.get(viewType); - if (!provider) { - throw new Error(`No provider for ${viewType} registered`); - } - - provider.dispose(); - this._editorProviders.delete(viewType); - - this._customEditorService.models.disposeAllModelsForView(viewType); - } - - private async getOrCreateCustomEditorModel( - modelType: ModelType, - resource: URI, - viewType: string, - options: { backupId?: string }, - cancellation: CancellationToken, - ): Promise> { - const existingModel = this._customEditorService.models.tryRetain(resource, viewType); - if (existingModel) { - return existingModel; - } - - switch (modelType) { - case ModelType.Text: - { - const model = CustomTextEditorModel.create(this._instantiationService, viewType, resource); - return this._customEditorService.models.add(resource, viewType, model); - } - case ModelType.Custom: - { - const model = MainThreadCustomEditorModel.create(this._instantiationService, this._proxyCustomEditors, viewType, resource, options, () => { - return Array.from(this._webviewInputs) - .filter(editor => editor instanceof CustomEditorInput && isEqual(editor.resource, resource)) as CustomEditorInput[]; - }, cancellation, this._backupService); - return this._customEditorService.models.add(resource, viewType, model); - } - } + this.customEditors.$unregisterEditorProvider(viewType); } public async $onDidEdit(resourceComponents: UriComponents, viewType: string, editId: number, label: string | undefined): Promise { - const model = await this.getCustomEditorModel(resourceComponents, viewType); - model.pushEdit(editId, label); + this.customEditors.$onDidEdit(resourceComponents, viewType, editId, label); } public async $onContentChange(resourceComponents: UriComponents, viewType: string): Promise { - const model = await this.getCustomEditorModel(resourceComponents, viewType); - model.changeContent(); + this.customEditors.$onContentChange(resourceComponents, viewType); } private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewPanelHandle, webview: WebviewOverlay) { @@ -596,15 +435,6 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma return this._webviewInputs.getInputForHandle(handle); } - private async getCustomEditorModel(resourceComponents: UriComponents, viewType: string) { - const resource = URI.revive(resourceComponents); - const model = await this._customEditorService.models.get(resource, viewType); - if (!model || !(model instanceof MainThreadCustomEditorModel)) { - throw new Error('Could not find model for webview editor'); - } - return model; - } - public getWebviewResolvedFailedContent(viewType: string) { return ` @@ -637,371 +467,3 @@ function reviveWebviewIcon( : undefined; } -namespace HotExitState { - export const enum Type { - Allowed, - NotAllowed, - Pending, - } - - export const Allowed = Object.freeze({ type: Type.Allowed } as const); - export const NotAllowed = Object.freeze({ type: Type.NotAllowed } as const); - - export class Pending { - readonly type = Type.Pending; - - constructor( - public readonly operation: CancelablePromise, - ) { } - } - - export type State = typeof Allowed | typeof NotAllowed | Pending; -} - - -class MainThreadCustomEditorModel extends Disposable implements ICustomEditorModel, IWorkingCopy { - - private _fromBackup: boolean = false; - private _hotExitState: HotExitState.State = HotExitState.Allowed; - private _backupId: string | undefined; - - private _currentEditIndex: number = -1; - private _savePoint: number = -1; - private readonly _edits: Array = []; - private _isDirtyFromContentChange = false; - - private _ongoingSave?: CancelablePromise; - - public static async create( - instantiationService: IInstantiationService, - proxy: extHostProtocol.ExtHostCustomEditorsShape, - viewType: string, - resource: URI, - options: { backupId?: string }, - getEditors: () => CustomEditorInput[], - cancellation: CancellationToken, - _backupFileService: IBackupFileService, - ) { - const { editable } = await proxy.$createCustomDocument(resource, viewType, options.backupId, cancellation); - return instantiationService.createInstance(MainThreadCustomEditorModel, proxy, viewType, resource, !!options.backupId, editable, getEditors); - } - - constructor( - private readonly _proxy: extHostProtocol.ExtHostCustomEditorsShape, - private readonly _viewType: string, - private readonly _editorResource: URI, - fromBackup: boolean, - private readonly _editable: boolean, - private readonly _getEditors: () => CustomEditorInput[], - @IFileDialogService private readonly _fileDialogService: IFileDialogService, - @IFileService private readonly _fileService: IFileService, - @ILabelService private readonly _labelService: ILabelService, - @IUndoRedoService private readonly _undoService: IUndoRedoService, - @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, - @IWorkingCopyService workingCopyService: IWorkingCopyService, - ) { - super(); - - this._fromBackup = fromBackup; - - if (_editable) { - this._register(workingCopyService.registerWorkingCopy(this)); - } - } - - get editorResource() { - return this._editorResource; - } - - dispose() { - if (this._editable) { - this._undoService.removeElements(this._editorResource); - } - this._proxy.$disposeCustomDocument(this._editorResource, this._viewType); - super.dispose(); - } - - //#region IWorkingCopy - - public get resource() { - // Make sure each custom editor has a unique resource for backup and edits - return MainThreadCustomEditorModel.toWorkingCopyResource(this._viewType, this._editorResource); - } - - private static toWorkingCopyResource(viewType: string, resource: URI) { - return URI.from({ - scheme: Schemas.vscodeCustomEditor, - authority: viewType, - path: resource.path, - query: JSON.stringify(resource.toJSON()), - }); - } - - public get name() { - return basename(this._labelService.getUriLabel(this._editorResource)); - } - - public get capabilities(): WorkingCopyCapabilities { - return 0; - } - - public isDirty(): boolean { - if (this._isDirtyFromContentChange) { - return true; - } - if (this._edits.length > 0) { - return this._savePoint !== this._currentEditIndex; - } - return this._fromBackup; - } - - private readonly _onDidChangeDirty: Emitter = this._register(new Emitter()); - readonly onDidChangeDirty: Event = this._onDidChangeDirty.event; - - private readonly _onDidChangeContent: Emitter = this._register(new Emitter()); - readonly onDidChangeContent: Event = this._onDidChangeContent.event; - - //#endregion - - public isReadonly() { - return !this._editable; - } - - public get viewType() { - return this._viewType; - } - - public get backupId() { - return this._backupId; - } - - public pushEdit(editId: number, label: string | undefined) { - if (!this._editable) { - throw new Error('Document is not editable'); - } - - this.change(() => { - this.spliceEdits(editId); - this._currentEditIndex = this._edits.length - 1; - }); - - this._undoService.pushElement({ - type: UndoRedoElementType.Resource, - resource: this._editorResource, - label: label ?? localize('defaultEditLabel', "Edit"), - undo: () => this.undo(), - redo: () => this.redo(), - }); - } - - public changeContent() { - this.change(() => { - this._isDirtyFromContentChange = true; - }); - } - - private async undo(): Promise { - if (!this._editable) { - return; - } - - if (this._currentEditIndex < 0) { - // nothing to undo - return; - } - - const undoneEdit = this._edits[this._currentEditIndex]; - this.change(() => { - --this._currentEditIndex; - }); - await this._proxy.$undo(this._editorResource, this.viewType, undoneEdit, this.isDirty()); - } - - private async redo(): Promise { - if (!this._editable) { - return; - } - - if (this._currentEditIndex >= this._edits.length - 1) { - // nothing to redo - return; - } - - const redoneEdit = this._edits[this._currentEditIndex + 1]; - this.change(() => { - ++this._currentEditIndex; - }); - await this._proxy.$redo(this._editorResource, this.viewType, redoneEdit, this.isDirty()); - } - - private spliceEdits(editToInsert?: number) { - const start = this._currentEditIndex + 1; - const toRemove = this._edits.length - this._currentEditIndex; - - const removedEdits = typeof editToInsert === 'number' - ? this._edits.splice(start, toRemove, editToInsert) - : this._edits.splice(start, toRemove); - - if (removedEdits.length) { - this._proxy.$disposeEdits(this._editorResource, this._viewType, removedEdits); - } - } - - private change(makeEdit: () => void): void { - const wasDirty = this.isDirty(); - makeEdit(); - this._onDidChangeContent.fire(); - - if (this.isDirty() !== wasDirty) { - this._onDidChangeDirty.fire(); - } - } - - public async revert(_options?: IRevertOptions) { - if (!this._editable) { - return; - } - - if (this._currentEditIndex === this._savePoint && !this._isDirtyFromContentChange && !this._fromBackup) { - return; - } - - this._proxy.$revert(this._editorResource, this.viewType, CancellationToken.None); - this.change(() => { - this._isDirtyFromContentChange = false; - this._fromBackup = false; - this._currentEditIndex = this._savePoint; - this.spliceEdits(); - }); - } - - public async save(options?: ISaveOptions): Promise { - return !!await this.saveCustomEditor(options); - } - - public async saveCustomEditor(options?: ISaveOptions): Promise { - if (!this._editable) { - return undefined; - } - - if (this._editorResource.scheme === Schemas.untitled) { - const targetUri = await this.suggestUntitledSavePath(options); - if (!targetUri) { - return undefined; - } - - await this.saveCustomEditorAs(this._editorResource, targetUri, options); - return targetUri; - } - - const savePromise = createCancelablePromise(token => this._proxy.$onSave(this._editorResource, this.viewType, token)); - this._ongoingSave?.cancel(); - this._ongoingSave = savePromise; - - this.change(() => { - this._isDirtyFromContentChange = false; - this._savePoint = this._currentEditIndex; - this._fromBackup = false; - }); - - try { - await savePromise; - } finally { - if (this._ongoingSave === savePromise) { - this._ongoingSave = undefined; - } - } - return this._editorResource; - } - - private suggestUntitledSavePath(options: ISaveOptions | undefined): Promise { - if (this._editorResource.scheme !== Schemas.untitled) { - throw new Error('Resource is not untitled'); - } - - const remoteAuthority = this._environmentService.configuration.remoteAuthority; - const localResrouce = toLocalResource(this._editorResource, remoteAuthority); - - - return this._fileDialogService.pickFileToSave(localResrouce, options?.availableFileSystems); - } - - public async saveCustomEditorAs(resource: URI, targetResource: URI, _options?: ISaveOptions): Promise { - if (this._editable) { - // TODO: handle cancellation - await createCancelablePromise(token => this._proxy.$onSaveAs(this._editorResource, this.viewType, targetResource, token)); - this.change(() => { - this._savePoint = this._currentEditIndex; - }); - return true; - } else { - // Since the editor is readonly, just copy the file over - await this._fileService.copy(resource, targetResource, false /* overwrite */); - return true; - } - } - - public async backup(): Promise { - const editors = this._getEditors(); - if (!editors.length) { - throw new Error('No editors found for resource, cannot back up'); - } - const primaryEditor = editors[0]; - - const backupData: IWorkingCopyBackup = { - meta: { - viewType: this.viewType, - editorResource: this._editorResource, - backupId: '', - extension: primaryEditor.extension ? { - id: primaryEditor.extension.id.value, - location: primaryEditor.extension.location, - } : undefined, - webview: { - id: primaryEditor.id, - options: primaryEditor.webview.options, - state: primaryEditor.webview.state, - } - } - }; - - if (!this._editable) { - return backupData; - } - - if (this._hotExitState.type === HotExitState.Type.Pending) { - this._hotExitState.operation.cancel(); - } - - const pendingState = new HotExitState.Pending( - createCancelablePromise(token => - this._proxy.$backup(this._editorResource.toJSON(), this.viewType, token))); - this._hotExitState = pendingState; - - try { - const backupId = await pendingState.operation; - // Make sure state has not changed in the meantime - if (this._hotExitState === pendingState) { - this._hotExitState = HotExitState.Allowed; - backupData.meta!.backupId = backupId; - this._backupId = backupId; - } - } catch (e) { - if (isPromiseCanceledError(e)) { - // This is expected - throw e; - } - - // Otherwise it could be a real error. Make sure state has not changed in the meantime. - if (this._hotExitState === pendingState) { - this._hotExitState = HotExitState.NotAllowed; - } - } - - if (this._hotExitState === HotExitState.Allowed) { - return backupData; - } - - throw new Error('Cannot back up in this state'); - } -} From 2489ff75c9d55ef3168520c5fffc47e0b119fdad Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 21 Aug 2020 14:48:08 -0700 Subject: [PATCH 440/736] Add setting to control cell toolbar position Fix #105195 --- .../api/browser/mainThreadNotebook.ts | 6 ++--- .../notebook/browser/media/notebook.css | 13 ++++++++++- .../notebook/browser/notebook.contribution.ts | 12 +++++++--- .../notebook/browser/notebookEditorWidget.ts | 23 ++++++++++++++++++- .../notebook/browser/notebookServiceImpl.ts | 6 ++--- .../contrib/notebook/common/notebookCommon.ts | 3 +++ 6 files changed, 52 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index d616a3ba793..1fdf3135d25 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -9,7 +9,7 @@ import { MainContext, MainThreadNotebookShape, NotebookExtensionDescription, IEx import { Disposable, IDisposable, combinedDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import { INotebookService, IMainNotebookController } from 'vs/workbench/contrib/notebook/common/notebookService'; -import { NOTEBOOK_DISPLAY_ORDER, NotebookCellOutputsSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, INotebookKernelInfo, INotebookKernelInfoDto, IEditor, INotebookDocumentFilter } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NOTEBOOK_DISPLAY_ORDER, NotebookCellOutputsSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, INotebookKernelInfo, INotebookKernelInfoDto, IEditor, INotebookDocumentFilter, DisplayOrderKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -311,7 +311,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo })); const updateOrder = () => { - let userOrder = this.configurationService.getValue('notebook.displayOrder'); + let userOrder = this.configurationService.getValue(DisplayOrderKey); this._proxy.$acceptDisplayOrder({ defaultOrder: this.accessibilityService.isScreenReaderOptimized() ? ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER : NOTEBOOK_DISPLAY_ORDER, userOrder: userOrder @@ -321,7 +321,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo updateOrder(); this._register(this.configurationService.onDidChangeConfiguration(e => { - if (e.affectedKeys.indexOf('notebook.displayOrder') >= 0) { + if (e.affectedKeys.indexOf(DisplayOrderKey) >= 0) { updateOrder(); } })); diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 4e23c35c249..7c9aa235e5f 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -298,12 +298,23 @@ display: inline-flex; position: absolute; height: 26px; - right: 44px; top: -12px; /* this lines up the bottom toolbar border with the current line when on line 01 */ z-index: 30; } +.monaco-workbench .notebookOverlay.cell-title-toolbar-right > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-title-toolbar { + right: 44px; +} + +.monaco-workbench .notebookOverlay.cell-title-toolbar-left > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-title-toolbar { + left: 76px; +} + +.monaco-workbench .notebookOverlay.cell-title-toolbar-hidden > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-title-toolbar { + display: none; +} + .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-title-toolbar .action-item { width: 24px; height: 24px; diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index e5d962b3ff4..2420ce81f4f 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -30,7 +30,7 @@ import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEd import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { NotebookService } from 'vs/workbench/contrib/notebook/browser/notebookServiceImpl'; -import { CellKind, CellUri, getCellUndoRedoComparisonKey, NotebookDocumentBackupData, NotebookEditorPriority } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellToolbarLocKey, CellUri, DisplayOrderKey, getCellUndoRedoComparisonKey, NotebookDocumentBackupData, NotebookEditorPriority } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService, IOpenEditorOverride } from 'vs/workbench/services/editor/common/editorService'; @@ -385,13 +385,19 @@ configurationRegistry.registerConfiguration({ title: nls.localize('notebookConfigurationTitle', "Notebook"), type: 'object', properties: { - 'notebook.displayOrder': { - markdownDescription: nls.localize('notebook.displayOrder.description', "Priority list for output mime types"), + [DisplayOrderKey]: { + description: nls.localize('notebook.displayOrder.description', "Priority list for output mime types"), type: ['array'], items: { type: 'string' }, default: [] + }, + [CellToolbarLocKey]: { + description: nls.localize('notebook.cellToolbarLocation.description', "Where the cell toolbar should be shown, or whether it should be hidden."), + type: 'string', + enum: ['left', 'right', 'hidden'], + default: 'right' } } }); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 79f96f2d22d..7f14b13ebdf 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -36,7 +36,7 @@ import { CodeCellRenderer, MarkdownCellRenderer, NotebookCellListDelegate, ListT import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { CellViewModel, IModelDecorationsChangeAccessor, INotebookEditorViewState, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { CellKind, IProcessedOutput, INotebookKernelInfo, INotebookKernelInfoDto, INotebookKernelInfo2, NotebookRunState, NotebookCellRunState, IInsetRenderOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, IProcessedOutput, INotebookKernelInfo, INotebookKernelInfoDto, INotebookKernelInfo2, NotebookRunState, NotebookCellRunState, IInsetRenderOutput, CellToolbarLocKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -229,6 +229,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this.layout(this._dimension); } } + + if (e.affectsConfiguration(CellToolbarLocKey)) { + this._updateForNotebookConfiguration(); + } }); this.notebookService.addNotebookEditor(this); @@ -267,6 +271,21 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return true; } + private _updateForNotebookConfiguration() { + if (!this._overlayContainer) { + return; + } + + const cellToolbarLocation = this.configurationService.getValue(CellToolbarLocKey); + this._overlayContainer.classList.remove('cell-title-toolbar-left'); + this._overlayContainer.classList.remove('cell-title-toolbar-right'); + this._overlayContainer.classList.remove('cell-title-toolbar-hidden'); + + if (cellToolbarLocation === 'left' || cellToolbarLocation === 'right' || cellToolbarLocation === 'hidden') { + this._overlayContainer.classList.add(`cell-title-toolbar-${cellToolbarLocation}`); + } + } + updateEditorFocus() { // Note - focus going to the webview will fire 'blur', but the webview element will be // a descendent of the notebook editor root. @@ -355,6 +374,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor onUnexpectedError(err); } } + + this._updateForNotebookConfiguration(); } private _generateFontInfo(): void { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 4fdbe29a434..2fab79b87ab 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -27,7 +27,7 @@ import { NotebookKernelProviderAssociationRegistry, NotebookViewTypesExtensionRe import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellEditType, CellOutputKind, CellUri, ICellEditOperation, IDisplayOutput, INotebookKernelInfo, INotebookKernelInfo2, INotebookKernelProvider, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, NotebookCellOutputsSplice, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellEditType, CellOutputKind, CellUri, DisplayOrderKey, ICellEditOperation, IDisplayOutput, INotebookKernelInfo, INotebookKernelInfo2, INotebookKernelProvider, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, NotebookCellOutputsSplice, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer'; import { NotebookEditorDescriptor, NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; @@ -313,7 +313,7 @@ export class NotebookService extends Disposable implements INotebookService, ICu this._editorService.registerCustomEditorViewTypesHandler('Notebook', this); const updateOrder = () => { - const userOrder = this._configurationService.getValue('notebook.displayOrder'); + const userOrder = this._configurationService.getValue(DisplayOrderKey); this._displayOrder = { defaultOrder: this._accessibilityService.isScreenReaderOptimized() ? ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER : NOTEBOOK_DISPLAY_ORDER, userOrder: userOrder @@ -323,7 +323,7 @@ export class NotebookService extends Disposable implements INotebookService, ICu updateOrder(); this._register(this._configurationService.onDidChangeConfiguration(e => { - if (e.affectedKeys.indexOf('notebook.displayOrder') >= 0) { + if (e.affectedKeys.indexOf(DisplayOrderKey) >= 0) { updateOrder(); } })); diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 68b34d4a5d3..4ae28b0e979 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -715,3 +715,6 @@ export interface INotebookKernelProvider { executeNotebook(uri: URI, kernelId: string, handle: number | undefined): Promise; cancelNotebook(uri: URI, kernelId: string, handle: number | undefined): Promise; } + +export const DisplayOrderKey = 'notebook.displayOrder'; +export const CellToolbarLocKey = 'notebook.cellToolbarLocation'; From 14e50154fd47c1920c5fd5b5b519b8d8e3b519e0 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 21 Aug 2020 14:56:44 -0700 Subject: [PATCH 441/736] Fix language picker layout 1px overlap --- .../contrib/notebook/browser/media/notebook.css | 3 ++- .../notebook/browser/notebookEditorWidget.ts | 14 +++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 7c9aa235e5f..3471256f2e3 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -338,7 +338,7 @@ } .monaco-workbench .notebookOverlay .cell-statusbar-container { - height: 21px; + height: 22px; font-size: 12px; display: flex; position: relative; @@ -355,6 +355,7 @@ } .monaco-workbench .notebookOverlay .cell-statusbar-container .cell-language-picker { + height: 21px; /* Editor outline is -1px in, don't overlap */ padding: 0px 6px; cursor: pointer; } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 7f14b13ebdf..d2ad1e4b1b7 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1790,14 +1790,6 @@ registerThemingParticipant((theme, collector) => { box-sizing: border-box; }`); - // const color = getExtraColor(theme, embeddedEditorBackground, { dark: 'rgba(0, 0, 0, .4)', extra_dark: 'rgba(200, 235, 255, .064)', light: '#f4f4f4', hc: null }); - const color = theme.getColor(editorBackground); - if (color) { - collector.addRule(`.notebookOverlay .cell .monaco-editor-background, - .notebookOverlay .cell .margin-view-overlays, - .notebookOverlay .cell .cell-statusbar-container { background: ${color}; }`); - collector.addRule(`.notebookOverlay .cell-drag-image .cell-editor-container > div { background: ${color} !important; }`); - } const link = theme.getColor(textLinkForeground); if (link) { collector.addRule(`.notebookOverlay .output a, @@ -1834,7 +1826,11 @@ registerThemingParticipant((theme, collector) => { const editorBackgroundColor = theme.getColor(editorBackground); if (editorBackgroundColor) { - collector.addRule(`.notebookOverlay .cell-statusbar-container { border-top: solid 1px ${editorBackgroundColor}; }`); + collector.addRule(`.notebookOverlay .cell .monaco-editor-background, + .notebookOverlay .cell .margin-view-overlays, + .notebookOverlay .cell .cell-statusbar-container { background: ${editorBackgroundColor}; }`); + collector.addRule(`.notebookOverlay .cell-drag-image .cell-editor-container > div { background: ${editorBackgroundColor} !important; }`); + collector.addRule(`.notebookOverlay .monaco-list-row .cell-title-toolbar { background-color: ${editorBackgroundColor}; }`); collector.addRule(`.notebookOverlay .monaco-list-row.cell-drag-image { background-color: ${editorBackgroundColor}; }`); collector.addRule(`.notebookOverlay .cell-bottom-toolbar-container .action-item { background-color: ${editorBackgroundColor} }`); From 5ec7e9623870dea3089a8b6306b8c770dcaa35d4 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 21 Aug 2020 12:23:22 -0700 Subject: [PATCH 442/736] Spelling --- src/vs/workbench/api/browser/mainThreadCustomEditors.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts index 8e26e31cd18..f21b61d7775 100644 --- a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts @@ -512,10 +512,9 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod } const remoteAuthority = this._environmentService.configuration.remoteAuthority; - const localResrouce = toLocalResource(this._editorResource, remoteAuthority); + const localResource = toLocalResource(this._editorResource, remoteAuthority); - - return this._fileDialogService.pickFileToSave(localResrouce, options?.availableFileSystems); + return this._fileDialogService.pickFileToSave(localResource, options?.availableFileSystems); } public async saveCustomEditorAs(resource: URI, targetResource: URI, _options?: ISaveOptions): Promise { From 041cf5dd0f2cfcf37329f5280848f5a1aa83100d Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 21 Aug 2020 15:22:28 -0700 Subject: [PATCH 443/736] Split main thread webview views to own file --- .../api/browser/mainThreadCustomEditors.ts | 1 - .../api/browser/mainThreadWebview.ts | 91 +++++------------- .../api/browser/mainThreadWebviewViews.ts | 92 +++++++++++++++++++ 3 files changed, 113 insertions(+), 71 deletions(-) create mode 100644 src/vs/workbench/api/browser/mainThreadWebviewViews.ts diff --git a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts index f21b61d7775..620922edc5e 100644 --- a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts @@ -97,7 +97,6 @@ export class MainThreadCustomEditors extends Disposable { throw new Error(`Provider for ${viewType} already registered`); } - const disposables = new DisposableStore(); disposables.add(this._customEditorService.registerCustomEditorCapabilities(viewType, { diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index f2a43a2160a..5fedd8e6935 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancellationToken } from 'vs/base/common/cancellation'; -import { onUnexpectedError } from 'vs/base/common/errors'; import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { isWeb } from 'vs/base/common/platform'; @@ -17,8 +15,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IProductService } from 'vs/platform/product/common/productService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { MainThreadCustomEditors, CustomEditorModelType } from 'vs/workbench/api/browser/mainThreadCustomEditors'; +import { CustomEditorModelType, MainThreadCustomEditors } from 'vs/workbench/api/browser/mainThreadCustomEditors'; import { MainThreadWebviewSerializers } from 'vs/workbench/api/browser/mainThreadWebviewSerializer'; +import { MainThreadWebviewsViews } from 'vs/workbench/api/browser/mainThreadWebviewViews'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; import { IEditorInput } from 'vs/workbench/common/editor'; @@ -26,7 +25,6 @@ import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { Webview, WebviewExtensionDescription, WebviewIcons, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; import { ICreateWebViewShowOptions, IWebviewWorkbenchService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; -import { IWebviewViewService, WebviewView } from 'vs/workbench/contrib/webviewView/browser/webviewViewService'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { extHostNamedCustomer } from '../common/extHostCustomers'; @@ -98,18 +96,16 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma public readonly webviewPanelViewType = new WebviewViewTypeTransformer('mainThreadWebview-'); private readonly _proxy: extHostProtocol.ExtHostWebviewsShape; - private readonly _proxyViews: extHostProtocol.ExtHostWebviewViewsShape; + private readonly _webviews = new Map(); private readonly _webviewInputs = new WebviewInputStore(); - private readonly _webviewViewProviders = new Map(); - private readonly _webviewViews = new Map(); - private readonly _editorProviders = new Map(); private readonly _webviewFromDiffEditorHandles = new Set(); private readonly serializers: MainThreadWebviewSerializers; private readonly customEditors: MainThreadCustomEditors; + private readonly webviewViews: MainThreadWebviewsViews; constructor( context: extHostProtocol.IExtHostContext, @@ -120,15 +116,14 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma @ITelemetryService private readonly _telemetryService: ITelemetryService, @IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IWebviewViewService private readonly _webviewViewService: IWebviewViewService, ) { super(); + this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviews); + this.serializers = this._instantiationService.createInstance(MainThreadWebviewSerializers, this, context); this.customEditors = this._instantiationService.createInstance(MainThreadCustomEditors, this, context); - - this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviews); - this._proxyViews = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewViews); + this.webviewViews = this._instantiationService.createInstance(MainThreadWebviewsViews, this, context); this._register(_editorService.onDidActiveEditorChange(() => { const activeInput = this._editorService.activeEditor; @@ -142,7 +137,6 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma this._register(_editorService.onDidVisibleEditorsChange(() => { this.updateWebviewViewStates(this._editorService.activeEditor); })); - } dispose() { @@ -158,7 +152,12 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma public addWebviewInput(handle: extHostProtocol.WebviewPanelHandle, input: WebviewInput): void { this._webviewInputs.add(handle, input); - this.hookupWebviewEventDelegate(handle, input.webview); + this.addWebview(handle, input.webview); + } + + public addWebview(handle: extHostProtocol.WebviewPanelHandle, webview: WebviewOverlay): void { + this._webviews.set(handle, webview); + this.hookupWebviewEventDelegate(handle, webview); } public $createWebviewPanel( @@ -200,11 +199,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma } public $setWebviewViewTitle(handle: extHostProtocol.WebviewPanelHandle, value: string | undefined): void { - const webviewView = this._webviewViews.get(handle); - if (!webviewView) { - throw new Error('unknown webview view'); - } - webviewView.title = value; + this.webviewViews.$setWebviewViewTitle(handle, value); } public $setIconPath(handle: extHostProtocol.WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents; } | undefined): void { @@ -240,65 +235,20 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma return true; } - $registerSerializer(viewType: string): void { + public $registerSerializer(viewType: string): void { this.serializers.$registerSerializer(viewType); } - $unregisterSerializer(viewType: string): void { + public $unregisterSerializer(viewType: string): void { this.serializers.$unregisterSerializer(viewType); } public $registerWebviewViewProvider(viewType: string, options?: { retainContextWhenHidden?: boolean }): void { - if (this._webviewViewProviders.has(viewType)) { - throw new Error(`View provider for ${viewType} already registered`); - } - - this._webviewViewService.register(viewType, { - resolve: async (webviewView: WebviewView, cancellation: CancellationToken) => { - this._webviewViews.set(viewType, webviewView); - - const handle = viewType; - this.hookupWebviewEventDelegate(handle, webviewView.webview); - - let state = undefined; - if (webviewView.webview.state) { - try { - state = JSON.parse(webviewView.webview.state); - } catch (e) { - console.error('Could not load webview state', e, webviewView.webview.state); - } - } - - if (options) { - webviewView.webview.options = options; - } - - webviewView.onDidChangeVisibility(visible => { - this._proxyViews.$onDidChangeWebviewViewVisibility(handle, visible); - }); - - webviewView.onDispose(() => { - this._proxyViews.$disposeWebviewView(handle); - }); - - try { - await this._proxyViews.$resolveWebviewView(handle, viewType, state, cancellation); - } catch (error) { - onUnexpectedError(error); - webviewView.webview.html = this.getWebviewResolvedFailedContent(viewType); - } - } - }); + this.webviewViews.$registerWebviewViewProvider(viewType, options); } public $unregisterWebviewViewProvider(viewType: string): void { - const provider = this._webviewViewProviders.get(viewType); - if (!provider) { - throw new Error(`No view provider for ${viewType} registered`); - } - - provider.dispose(); - this._webviewViewProviders.delete(viewType); + this.webviewViews.$unregisterWebviewViewProvider(viewType); } public $registerTextEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: extHostProtocol.CustomTextEditorCapabilities): void { @@ -321,7 +271,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma this.customEditors.$onContentChange(resourceComponents, viewType); } - private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewPanelHandle, webview: WebviewOverlay) { + public hookupWebviewEventDelegate(handle: extHostProtocol.WebviewPanelHandle, webview: WebviewOverlay) { const disposables = new DisposableStore(); disposables.add(webview.onDidClickLink((uri) => this.onDidClickLink(handle, uri))); @@ -332,6 +282,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma disposables.dispose(); this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => { + this._webviews.delete(handle); this._webviewInputs.delete(handle); }); })); @@ -416,7 +367,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma } private getWebview(handle: extHostProtocol.WebviewPanelHandle): Webview { - const webview = this.tryGetWebviewInput(handle)?.webview ?? this._webviewViews.get(handle)?.webview; + const webview = this._webviews.get(handle); if (!webview) { throw new Error(`Unknown webview handle:${handle}`); } diff --git a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts new file mode 100644 index 00000000000..59aba1a8b43 --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts @@ -0,0 +1,92 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken } from 'vs/base/common/cancellation'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import type { MainThreadWebviews } from 'vs/workbench/api/browser/mainThreadWebview'; +import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; +import { IWebviewViewService, WebviewView } from 'vs/workbench/contrib/webviewView/browser/webviewViewService'; + + +export class MainThreadWebviewsViews extends Disposable { + + private readonly _proxyViews: extHostProtocol.ExtHostWebviewViewsShape; + + private readonly _webviewViews = new Map(); + private readonly _webviewViewProviders = new Map(); + + constructor( + private readonly mainThreadWebviews: MainThreadWebviews, + context: extHostProtocol.IExtHostContext, + @IWebviewViewService private readonly _webviewViewService: IWebviewViewService, + ) { + super(); + + this._proxyViews = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewViews); + } + + public $setWebviewViewTitle(handle: extHostProtocol.WebviewPanelHandle, value: string | undefined): void { + const webviewView = this._webviewViews.get(handle); + if (!webviewView) { + throw new Error('unknown webview view'); + } + webviewView.title = value; + } + + public $registerWebviewViewProvider(viewType: string, options?: { retainContextWhenHidden?: boolean }): void { + if (this._webviewViewProviders.has(viewType)) { + throw new Error(`View provider for ${viewType} already registered`); + } + + this._webviewViewService.register(viewType, { + resolve: async (webviewView: WebviewView, cancellation: CancellationToken) => { + this._webviewViews.set(viewType, webviewView); + + const handle = viewType; + this.mainThreadWebviews.addWebview(handle, webviewView.webview); + + let state = undefined; + if (webviewView.webview.state) { + try { + state = JSON.parse(webviewView.webview.state); + } catch (e) { + console.error('Could not load webview state', e, webviewView.webview.state); + } + } + + if (options) { + webviewView.webview.options = options; + } + + webviewView.onDidChangeVisibility(visible => { + this._proxyViews.$onDidChangeWebviewViewVisibility(handle, visible); + }); + + webviewView.onDispose(() => { + this._proxyViews.$disposeWebviewView(handle); + }); + + try { + await this._proxyViews.$resolveWebviewView(handle, viewType, state, cancellation); + } catch (error) { + onUnexpectedError(error); + webviewView.webview.html = this.mainThreadWebviews.getWebviewResolvedFailedContent(viewType); + } + } + }); + } + + public $unregisterWebviewViewProvider(viewType: string): void { + const provider = this._webviewViewProviders.get(viewType); + if (!provider) { + throw new Error(`No view provider for ${viewType} registered`); + } + + provider.dispose(); + this._webviewViewProviders.delete(viewType); + } +} + From c50b5acd377190bfe2d7c9eceddccb909540a041 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 21 Aug 2020 16:40:55 -0700 Subject: [PATCH 444/736] Fix local resources in webview views Ensure we use the correct id for identifying the webview internally --- src/vs/workbench/api/browser/mainThreadWebviewViews.ts | 2 +- src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts | 3 +-- .../contrib/webview/browser/dynamicWebviewEditorOverlay.ts | 2 +- src/vs/workbench/contrib/webview/browser/webview.ts | 3 +++ 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts index 59aba1a8b43..c882626890d 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts @@ -45,7 +45,7 @@ export class MainThreadWebviewsViews extends Disposable { resolve: async (webviewView: WebviewView, cancellation: CancellationToken) => { this._webviewViews.set(viewType, webviewView); - const handle = viewType; + const handle = webviewView.webview.id; this.mainThreadWebviews.addWebview(handle, webviewView.webview); let state = undefined; diff --git a/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts b/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts index aaa5fb6d6b1..b9dcaf3f964 100644 --- a/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts @@ -81,8 +81,7 @@ export abstract class BaseWebview extends Disposable { protected content: WebviewContent; constructor( - // TODO: matb, this should not be protected. The only reason it needs to be is that the base class ends up using it in the call to createElement - protected readonly id: string, + public readonly id: string, options: WebviewOptions, contentOptions: WebviewContentOptions, public readonly extension: WebviewExtensionDescription | undefined, diff --git a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts index 453b1040c2e..7d2046676ff 100644 --- a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts +++ b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts @@ -39,7 +39,7 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv private _findWidgetVisible: IContextKey; public constructor( - private readonly id: string, + public readonly id: string, initialOptions: WebviewOptions, initialContentOptions: WebviewContentOptions, public readonly extension: WebviewExtensionDescription | undefined, diff --git a/src/vs/workbench/contrib/webview/browser/webview.ts b/src/vs/workbench/contrib/webview/browser/webview.ts index 381e70b6ec7..4df30f8be7b 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.ts @@ -86,6 +86,9 @@ export interface IDataLinkClickEvent { } export interface Webview extends IDisposable { + + readonly id: string; + html: string; contentOptions: WebviewContentOptions; localResourcesRoot: URI[]; From ad407028575a77ea387eb7cc219b323dc017b686 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 21 Aug 2020 18:47:20 -0700 Subject: [PATCH 445/736] debug: bump js-debug and use its auto attach by default once more --- extensions/debug-auto-launch/package.json | 2 +- extensions/debug-auto-launch/src/extension.ts | 2 +- product.json | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/debug-auto-launch/package.json b/extensions/debug-auto-launch/package.json index 2e958bb6d25..3cb11ef1844 100644 --- a/extensions/debug-auto-launch/package.json +++ b/extensions/debug-auto-launch/package.json @@ -39,7 +39,7 @@ "debug.javascript.usePreviewAutoAttach": { "scope": "window", "type": "boolean", - "default": false, + "default": true, "description": "%debug.javascript.usePreviewAutoAttach%" } } diff --git a/extensions/debug-auto-launch/src/extension.ts b/extensions/debug-auto-launch/src/extension.ts index 37be929aed8..f3527df7125 100644 --- a/extensions/debug-auto-launch/src/extension.ts +++ b/extensions/debug-auto-launch/src/extension.ts @@ -93,7 +93,7 @@ function toggleAutoAttachSetting() { function autoAttachWithJsDebug() { const jsDebugConfig = vscode.workspace.getConfiguration(JS_DEBUG_SETTINGS); - return jsDebugConfig.get(JS_DEBUG_USEPREVIEWAA, false); + return jsDebugConfig.get(JS_DEBUG_USEPREVIEWAA, true); } function readCurrentState(): State { diff --git a/product.json b/product.json index ddd0d291911..70c95edd0d4 100644 --- a/product.json +++ b/product.json @@ -76,7 +76,7 @@ }, { "name": "ms-vscode.js-debug-companion", - "version": "1.0.2", + "version": "1.0.4", "repo": "https://github.com/microsoft/vscode-js-debug-companion", "metadata": { "id": "99cb0b7f-7354-4278-b8da-6cc79972169d", @@ -91,7 +91,7 @@ }, { "name": "ms-vscode.js-debug", - "version": "1.48.1", + "version": "1.49.0", "repo": "https://github.com/Microsoft/vscode-js-debug", "metadata": { "id": "25629058-ddac-4e17-abba-74678e126c5d", From 8d2ff254c14f06926bcb4325709801f11cd3e441 Mon Sep 17 00:00:00 2001 From: "Marvin A. Ruder" Date: Sat, 22 Aug 2020 21:52:04 +0200 Subject: [PATCH 446/736] =?UTF-8?q?Changed=20description=20of=20=E2=80=9CC?= =?UTF-8?q?ommit=20Signing=E2=80=9D=20setting=20label=20to=20include=20X.5?= =?UTF-8?q?09.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- extensions/git/package.nls.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 0e8ea1c649e..c412586f43c 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -107,7 +107,7 @@ "config.smartCommitChanges.all": "Automatically stage all changes.", "config.smartCommitChanges.tracked": "Automatically stage tracked changes only.", "config.suggestSmartCommit": "Suggests to enable smart commit (commit all changes when there are no staged changes).", - "config.enableCommitSigning": "Enables commit signing with GPG.", + "config.enableCommitSigning": "Enables commit signing with GPG or X.509.", "config.discardAllScope": "Controls what changes are discarded by the `Discard all changes` command. `all` discards all changes. `tracked` discards only tracked files. `prompt` shows a prompt dialog every time the action is run.", "config.decorations.enabled": "Controls whether Git contributes colors and badges to the explorer and the open editors view.", "config.enableStatusBarSync": "Controls whether the Git Sync command appears in the status bar.", From f57a418b019926b38d2e6949265c33002a8f6f6b Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sun, 23 Aug 2020 09:40:13 -0700 Subject: [PATCH 447/736] Make regex unicode reference transformation case-insensitive Fix #104745 --- .../workbench/services/search/node/ripgrepTextSearchEngine.ts | 4 ++-- .../services/search/test/node/ripgrepTextSearchEngine.test.ts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts b/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts index 006b8fec0df..a22fd8ba3b9 100644 --- a/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts +++ b/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts @@ -504,7 +504,7 @@ export function spreadGlobComponents(globArg: string): string[] { export function unicodeEscapesToPCRE2(pattern: string): string { // Match \u1234 - const unicodePattern = /((?:[^\\]|^)(?:\\\\)*)\\u([a-z0-9]{4})/g; + const unicodePattern = /((?:[^\\]|^)(?:\\\\)*)\\u([a-z0-9]{4})/gi; while (pattern.match(unicodePattern)) { pattern = pattern.replace(unicodePattern, `$1\\x{$2}`); @@ -512,7 +512,7 @@ export function unicodeEscapesToPCRE2(pattern: string): string { // Match \u{1234} // \u with 5-6 characters will be left alone because \x only takes 4 characters. - const unicodePatternWithBraces = /((?:[^\\]|^)(?:\\\\)*)\\u\{([a-z0-9]{4})\}/g; + const unicodePatternWithBraces = /((?:[^\\]|^)(?:\\\\)*)\\u\{([a-z0-9]{4})\}/gi; while (pattern.match(unicodePatternWithBraces)) { pattern = pattern.replace(unicodePatternWithBraces, `$1\\x{$2}`); } diff --git a/src/vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts b/src/vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts index 05fc07f6214..6c7d8296a2d 100644 --- a/src/vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts +++ b/src/vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts @@ -20,6 +20,7 @@ suite('RipgrepTextSearchEngine', () => { assert.equal(unicodeEscapesToPCRE2('\\u{1234}'), '\\x{1234}'); assert.equal(unicodeEscapesToPCRE2('\\u{1234}\\u{0001}'), '\\x{1234}\\x{0001}'); assert.equal(unicodeEscapesToPCRE2('foo\\u{1234}bar'), 'foo\\x{1234}bar'); + assert.equal(unicodeEscapesToPCRE2('[\\u00A0-\\u00FF]'), '[\\x{00A0}-\\x{00FF}]'); assert.equal(unicodeEscapesToPCRE2('foo\\u{123456}7bar'), 'foo\\u{123456}7bar'); assert.equal(unicodeEscapesToPCRE2('\\u123'), '\\u123'); From 1689304b527a97e79317d5094b35eef00b433ea0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 24 Aug 2020 07:57:59 +0200 Subject: [PATCH 448/736] Allow to configure workbench.editor.restoreViewState per language (fix #101110) --- extensions/git/package.json | 6 ++- .../browser/parts/editor/baseEditor.ts | 6 ++- .../browser/parts/editor/binaryEditor.ts | 6 +-- .../browser/parts/editor/editorControl.ts | 10 ++--- .../browser/parts/editor/editorGroupView.ts | 12 +++--- .../browser/parts/editor/sideBySideEditor.ts | 26 ++++++------- .../browser/parts/editor/textDiffEditor.ts | 12 +++--- .../browser/parts/editor/textEditor.ts | 30 +++++++------- .../parts/editor/textResourceEditor.ts | 10 ++--- .../browser/workbench.contribution.ts | 3 +- src/vs/workbench/common/editor.ts | 16 ++++++++ src/vs/workbench/common/editor/editorGroup.ts | 39 ++++++++++++------- .../extensions/browser/extensionEditor.ts | 6 +-- .../files/browser/editors/textFileEditor.ts | 18 +++++---- .../notebook/browser/notebookEditor.ts | 6 +-- .../contrib/output/browser/outputView.ts | 8 ++-- .../preferences/browser/keybindingsEditor.ts | 6 +-- .../preferences/browser/preferencesEditor.ts | 24 ++++++------ .../preferences/browser/settingsEditor2.ts | 6 +-- .../searchEditor/browser/searchEditor.ts | 6 +-- .../contrib/webview/browser/webviewEditor.ts | 6 +-- .../walkThrough/browser/walkThroughPart.ts | 6 +-- .../browser/parts/editor/baseEditor.test.ts | 2 +- .../browser/parts/editor/editorGroups.test.ts | 21 +++++----- .../test/browser/workbenchTestServices.ts | 6 +-- 25 files changed, 168 insertions(+), 129 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 748868778dd..1704a89e7e3 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1915,7 +1915,11 @@ "[git-commit]": { "editor.rulers": [ 72 - ] + ], + "workbench.editor.restoreViewState": false + }, + "[git-rebase]": { + "workbench.editor.restoreViewState": false } }, "viewsWelcome": [ diff --git a/src/vs/workbench/browser/parts/editor/baseEditor.ts b/src/vs/workbench/browser/parts/editor/baseEditor.ts index cf48687419b..b3cce045b9a 100644 --- a/src/vs/workbench/browser/parts/editor/baseEditor.ts +++ b/src/vs/workbench/browser/parts/editor/baseEditor.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Composite } from 'vs/workbench/browser/composite'; -import { EditorInput, EditorOptions, IEditorPane, GroupIdentifier, IEditorMemento } from 'vs/workbench/common/editor'; +import { EditorInput, EditorOptions, IEditorPane, GroupIdentifier, IEditorMemento, IEditorOpenContext } from 'vs/workbench/common/editor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -91,10 +91,12 @@ export abstract class BaseEditor extends Composite implements IEditorPane { * to be different from the previous input that was set using the `input.matches()` * method. * + * The provided context gives more information around how the editor was opened. + * * The provided cancellation token should be used to test if the operation * was cancelled. */ - async setInput(input: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { + async setInput(input: EditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { this._input = input; this._options = options; } diff --git a/src/vs/workbench/browser/parts/editor/binaryEditor.ts b/src/vs/workbench/browser/parts/editor/binaryEditor.ts index ca270875dfb..c04fdac81bb 100644 --- a/src/vs/workbench/browser/parts/editor/binaryEditor.ts +++ b/src/vs/workbench/browser/parts/editor/binaryEditor.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/binaryeditor'; import * as nls from 'vs/nls'; import { Emitter } from 'vs/base/common/event'; -import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; +import { EditorInput, EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -74,8 +74,8 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor { parent.appendChild(this.scrollbar.getDomNode()); } - async setInput(input: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { - await super.setInput(input, options, token); + async setInput(input: EditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { + await super.setInput(input, options, context, token); const model = await input.resolve(); // Check for cancellation diff --git a/src/vs/workbench/browser/parts/editor/editorControl.ts b/src/vs/workbench/browser/parts/editor/editorControl.ts index b3a49e5d197..315c26376f9 100644 --- a/src/vs/workbench/browser/parts/editor/editorControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorControl.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { EditorInput, EditorOptions, IVisibleEditorPane } from 'vs/workbench/common/editor'; +import { EditorInput, EditorOptions, IEditorOpenContext, IVisibleEditorPane } from 'vs/workbench/common/editor'; import { Dimension, show, hide, addClass } from 'vs/base/browser/dom'; import { Registry } from 'vs/platform/registry/common/platform'; import { IEditorRegistry, Extensions as EditorExtensions, IEditorDescriptor } from 'vs/workbench/browser/editor'; @@ -53,7 +53,7 @@ export class EditorControl extends Disposable { super(); } - async openEditor(editor: EditorInput, options?: EditorOptions): Promise { + async openEditor(editor: EditorInput, options: EditorOptions | undefined, context: IEditorOpenContext): Promise { // Editor pane const descriptor = Registry.as(EditorExtensions.Editors).getEditor(editor); @@ -63,7 +63,7 @@ export class EditorControl extends Disposable { const editorPane = this.doShowEditorPane(descriptor); // Set input - const editorChanged = await this.doSetInput(editorPane, editor, options); + const editorChanged = await this.doSetInput(editorPane, editor, options, context); return { editorPane, editorChanged }; } @@ -147,7 +147,7 @@ export class EditorControl extends Disposable { this._onDidSizeConstraintsChange.fire(undefined); } - private async doSetInput(editorPane: BaseEditor, editor: EditorInput, options: EditorOptions | undefined): Promise { + private async doSetInput(editorPane: BaseEditor, editor: EditorInput, options: EditorOptions | undefined, context: IEditorOpenContext): Promise { // If the input did not change, return early and only apply the options // unless the options instruct us to force open it even if it is the same @@ -174,7 +174,7 @@ export class EditorControl extends Disposable { // Call into editor pane const editorWillChange = !inputMatches; try { - await editorPane.setInput(editor, options, operation.token); + await editorPane.setInput(editor, options, context, operation.token); // Focus (unless prevented or another operation is running) if (operation.isCurrent()) { diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 0bf99cb44ea..0fcb9999b13 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -459,7 +459,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { const activeElement = document.activeElement; // Show active editor - await this.doShowEditor(activeEditor, true, options); + await this.doShowEditor(activeEditor, { active: true, isNew: false /* restored */ }, options); // Set focused now if this is the active group and focus has // not changed meanwhile. This prevents focus from being @@ -954,10 +954,10 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Update model and make sure to continue to use the editor we get from // the model. It is possible that the editor was already opened and we // want to ensure that we use the existing instance in that case. - const openedEditor = this._group.openEditor(editor, openEditorOptions); + const { editor: openedEditor, isNew } = this._group.openEditor(editor, openEditorOptions); // Show editor - const showEditorResult = this.doShowEditor(openedEditor, !!openEditorOptions.active, options); + const showEditorResult = this.doShowEditor(openedEditor, { active: !!openEditorOptions.active, isNew }, options); // Finally make sure the group is active or restored as instructed if (activateGroup) { @@ -969,14 +969,14 @@ export class EditorGroupView extends Themable implements IEditorGroupView { return showEditorResult; } - private async doShowEditor(editor: EditorInput, active: boolean, options?: EditorOptions): Promise { + private async doShowEditor(editor: EditorInput, context: { active: boolean, isNew: boolean }, options?: EditorOptions): Promise { // Show in editor control if the active editor changed let openEditorPromise: Promise | undefined; - if (active) { + if (context.active) { openEditorPromise = (async () => { try { - const result = await this.editorControl.openEditor(editor, options); + const result = await this.editorControl.openEditor(editor, options, { newInGroup: context.isNew }); // Editor change event if (result.editorChanged) { diff --git a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts index aff171a997f..e0a8f911033 100644 --- a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts +++ b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts @@ -5,7 +5,7 @@ import * as DOM from 'vs/base/browser/dom'; import { Registry } from 'vs/platform/registry/common/platform'; -import { EditorInput, EditorOptions, SideBySideEditorInput, IEditorControl, IEditorPane } from 'vs/workbench/common/editor'; +import { EditorInput, EditorOptions, SideBySideEditorInput, IEditorControl, IEditorPane, IEditorOpenContext } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -94,11 +94,11 @@ export class SideBySideEditor extends BaseEditor { this.updateStyles(); } - async setInput(newInput: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { + async setInput(newInput: EditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { const oldInput = this.input as SideBySideEditorInput; - await super.setInput(newInput, options, token); + await super.setInput(newInput, options, context, token); - return this.updateInput(oldInput, (newInput as SideBySideEditorInput), options, token); + return this.updateInput(oldInput, (newInput as SideBySideEditorInput), options, context, token); } setOptions(options: EditorOptions | undefined): void { @@ -162,13 +162,13 @@ export class SideBySideEditor extends BaseEditor { return this.secondaryEditorPane; } - private async updateInput(oldInput: SideBySideEditorInput, newInput: SideBySideEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { + private async updateInput(oldInput: SideBySideEditorInput, newInput: SideBySideEditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { if (!newInput.matches(oldInput)) { if (oldInput) { this.disposeEditors(); } - return this.setNewInput(newInput, options, token); + return this.setNewInput(newInput, options, context, token); } if (!this.secondaryEditorPane || !this.primaryEditorPane) { @@ -176,16 +176,16 @@ export class SideBySideEditor extends BaseEditor { } await Promise.all([ - this.secondaryEditorPane.setInput(newInput.secondary, undefined, token), - this.primaryEditorPane.setInput(newInput.primary, options, token) + this.secondaryEditorPane.setInput(newInput.secondary, undefined, context, token), + this.primaryEditorPane.setInput(newInput.primary, options, context, token) ]); } - private setNewInput(newInput: SideBySideEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { + private setNewInput(newInput: SideBySideEditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { const secondaryEditor = this.doCreateEditor(newInput.secondary, assertIsDefined(this.secondaryEditorContainer)); const primaryEditor = this.doCreateEditor(newInput.primary, assertIsDefined(this.primaryEditorContainer)); - return this.onEditorsCreated(secondaryEditor, primaryEditor, newInput.secondary, newInput.primary, options, token); + return this.onEditorsCreated(secondaryEditor, primaryEditor, newInput.secondary, newInput.primary, options, context, token); } private doCreateEditor(editorInput: EditorInput, container: HTMLElement): BaseEditor { @@ -201,7 +201,7 @@ export class SideBySideEditor extends BaseEditor { return editor; } - private async onEditorsCreated(secondary: BaseEditor, primary: BaseEditor, secondaryInput: EditorInput, primaryInput: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { + private async onEditorsCreated(secondary: BaseEditor, primary: BaseEditor, secondaryInput: EditorInput, primaryInput: EditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { this.secondaryEditorPane = secondary; this.primaryEditorPane = primary; @@ -213,8 +213,8 @@ export class SideBySideEditor extends BaseEditor { this.onDidCreateEditors.fire(undefined); await Promise.all([ - this.secondaryEditorPane.setInput(secondaryInput, undefined, token), - this.primaryEditorPane.setInput(primaryInput, options, token)] + this.secondaryEditorPane.setInput(secondaryInput, undefined, context, token), + this.primaryEditorPane.setInput(primaryInput, options, context, token)] ); } diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index e03a4159dc4..22f107c6c88 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -9,7 +9,7 @@ import { isFunction, isObject, isArray, assertIsDefined } from 'vs/base/common/t import { IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IDiffEditorOptions, IEditorOptions as ICodeEditorOptions } from 'vs/editor/common/config/editorOptions'; import { BaseTextEditor, IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor'; -import { TextEditorOptions, EditorInput, EditorOptions, TEXT_DIFF_EDITOR_ID, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, ITextDiffEditorPane, IEditorInput } from 'vs/workbench/common/editor'; +import { TextEditorOptions, EditorInput, EditorOptions, TEXT_DIFF_EDITOR_ID, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, ITextDiffEditorPane, IEditorInput, IEditorOpenContext } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { DiffNavigator } from 'vs/editor/browser/widget/diffNavigator'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; @@ -72,7 +72,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan return this.instantiationService.createInstance(DiffEditorWidget, parent, configuration); } - async setInput(input: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { + async setInput(input: EditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { // Dispose previous diff navigator this.diffNavigatorDisposables.clear(); @@ -81,7 +81,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan this.doSaveOrClearTextDiffEditorViewState(this.input); // Set input and resolve - await super.setInput(input, options, token); + await super.setInput(input, options, context, token); try { const resolvedModel = await input.resolve(); @@ -107,9 +107,9 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan optionsGotApplied = (options).apply(diffEditor, ScrollType.Immediate); } - // Otherwise restore View State + // Otherwise restore View State unless disabled via settings let hasPreviousViewState = false; - if (!optionsGotApplied) { + if (!optionsGotApplied && this.shouldRestoreTextEditorViewState(input, context)) { hasPreviousViewState = this.restoreTextDiffEditorViewState(input, diffEditor); } @@ -288,7 +288,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan } // Clear view state if input is disposed or we are configured to not storing any state - if (input.isDisposed() || (!this.shouldRestoreViewState && (!this.group || !this.group.isOpened(input)))) { + if (input.isDisposed() || (!this.shouldRestoreTextEditorViewState(input) && (!this.group || !this.group.isOpened(input)))) { super.clearTextEditorViewState([resource], this.group); } diff --git a/src/vs/workbench/browser/parts/editor/textEditor.ts b/src/vs/workbench/browser/parts/editor/textEditor.ts index d3e594728ed..429b3125d23 100644 --- a/src/vs/workbench/browser/parts/editor/textEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textEditor.ts @@ -10,7 +10,7 @@ import { Event } from 'vs/base/common/event'; import { isObject, assertIsDefined, withNullAsUndefined, isFunction } from 'vs/base/common/types'; import { Dimension } from 'vs/base/browser/dom'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { EditorInput, EditorOptions, IEditorMemento, ITextEditorPane, TextEditorOptions, IEditorCloseEvent, IEditorInput, computeEditorAriaLabel } from 'vs/workbench/common/editor'; +import { EditorInput, EditorOptions, IEditorMemento, ITextEditorPane, TextEditorOptions, IEditorCloseEvent, IEditorInput, computeEditorAriaLabel, IEditorOpenContext, toResource, SideBySideEditor } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { IEditorViewState, IEditor, ScrollType } from 'vs/editor/common/editorCommon'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -47,9 +47,6 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditorPa private readonly groupListener = this._register(new MutableDisposable()); - private _shouldRestoreViewState: boolean | undefined; - protected get shouldRestoreViewState(): boolean | undefined { return this._shouldRestoreViewState; } - private _instantiationService: IInstantiationService; protected get instantiationService(): IInstantiationService { return this._instantiationService; } protected set instantiationService(value: IInstantiationService) { this._instantiationService = value; } @@ -69,7 +66,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditorPa this.editorMemento = this.getEditorMemento(editorGroupService, BaseTextEditor.TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY, 100); - this._register(this.textResourceConfigurationService.onDidChangeConfiguration(e => { + this._register(this.textResourceConfigurationService.onDidChangeConfiguration(() => { const resource = this.getActiveResource(); const value = resource ? this.textResourceConfigurationService.getValue(resource) : undefined; @@ -84,13 +81,9 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditorPa this.editorContainer?.setAttribute('aria-label', ariaLabel); this.editorControl?.updateOptions({ ariaLabel }); })); - - this.updateRestoreViewStateConfiguration(); } protected handleConfigurationChangeEvent(configuration?: IEditorConfiguration): void { - this.updateRestoreViewStateConfiguration(); - if (this.isVisible()) { this.updateEditorConfiguration(configuration); } else { @@ -98,10 +91,6 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditorPa } } - private updateRestoreViewStateConfiguration(): void { - this._shouldRestoreViewState = this.textResourceConfigurationService.getValue(undefined, 'workbench.editor.restoreViewState') ?? true /* default */; - } - private consumePendingConfigurationChangeEvent(): void { if (this.hasPendingConfigurationChange) { this.updateEditorConfiguration(); @@ -163,8 +152,8 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditorPa return this.instantiationService.createInstance(CodeEditorWidget, parent, configuration, {}); } - async setInput(input: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { - await super.setInput(input, options, token); + async setInput(input: EditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { + await super.setInput(input, options, context, token); // Update editor options after having set the input. We do this because there can be // editor input specific options (e.g. an ARIA label depending on the input showing) @@ -238,6 +227,17 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditorPa this.editorMemento.saveEditorState(this.group, resource, editorViewState); } + protected shouldRestoreTextEditorViewState(editor: IEditorInput, context?: IEditorOpenContext): boolean { + + // new editor: check with workbench.editor.restoreViewState setting + if (context?.newInGroup) { + return this.textResourceConfigurationService.getValue(toResource(editor, { supportSideBySide: SideBySideEditor.PRIMARY }), 'workbench.editor.restoreViewState') === false ? false : true /* restore by default */; + } + + // existing editor: always restore viewstate + return true; + } + getViewState(): IEditorViewState | undefined { const resource = this.input?.resource; if (resource) { diff --git a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts index bf60f8d5e3e..a99323c2ed9 100644 --- a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { assertIsDefined, isFunction, withNullAsUndefined } from 'vs/base/common/types'; import { ICodeEditor, getCodeEditor, IPasteEvent } from 'vs/editor/browser/editorBrowser'; -import { TextEditorOptions, EditorInput, EditorOptions } from 'vs/workbench/common/editor'; +import { TextEditorOptions, EditorInput, EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; @@ -54,13 +54,13 @@ export class AbstractTextResourceEditor extends BaseTextEditor { return nls.localize('textEditor', "Text Editor"); } - async setInput(input: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { + async setInput(input: EditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { // Remember view settings if input changes this.saveTextResourceEditorViewState(this.input); // Set input and resolve - await super.setInput(input, options, token); + await super.setInput(input, options, context, token); const resolvedModel = await input.resolve(); // Check for cancellation @@ -85,8 +85,8 @@ export class AbstractTextResourceEditor extends BaseTextEditor { optionsGotApplied = textOptions.apply(textEditor, ScrollType.Immediate); } - // Otherwise restore View State - if (!optionsGotApplied) { + // Otherwise restore View State unless disabled via settings + if (!optionsGotApplied && this.shouldRestoreTextEditorViewState(input, context)) { this.restoreTextResourceEditorViewState(input, textEditor); } diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 88050424cda..7656a699f3c 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -150,8 +150,9 @@ import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuratio }, '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."), + 'description': nls.localize('restoreViewState', "Restores the last view state (e.g. scroll position) when re-opening textual editors after they have been closed."), 'default': true, + 'scope': ConfigurationScope.LANGUAGE_OVERRIDABLE }, 'workbench.editor.centeredLayoutAutoResize': { 'type': 'boolean', diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 202f03996a9..8792fd3b5f1 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -1137,6 +1137,22 @@ export class TextEditorOptions extends EditorOptions implements ITextEditorOptio } } +/** + * Context passed into `BaseEditor#setInput` to give additional + * context information around why the editor was opened. + */ +export interface IEditorOpenContext { + + /** + * An indicator if the editor input is new for the group the editor is in. + * An editor is new for a group if it was not part of the group before and + * otherwise was already opened in the group and just became the active editor. + * + * This hint can e.g. be used to decide wether to restore view state or not. + */ + newInGroup?: boolean; +} + export interface IEditorIdentifier { groupId: GroupIdentifier; editor: IEditorInput; diff --git a/src/vs/workbench/common/editor/editorGroup.ts b/src/vs/workbench/common/editor/editorGroup.ts index 99d46391837..57e5432ed19 100644 --- a/src/vs/workbench/common/editor/editorGroup.ts +++ b/src/vs/workbench/common/editor/editorGroup.ts @@ -19,31 +19,36 @@ const EditorOpenPositioning = { }; export interface EditorCloseEvent extends IEditorCloseEvent { - editor: EditorInput; + readonly editor: EditorInput; } export interface EditorIdentifier extends IEditorIdentifier { - groupId: GroupIdentifier; - editor: EditorInput; + readonly groupId: GroupIdentifier; + readonly editor: EditorInput; } export interface IEditorOpenOptions { - pinned?: boolean; + readonly pinned?: boolean; sticky?: boolean; active?: boolean; - index?: number; + readonly index?: number; +} + +export interface IEditorOpenResult { + readonly editor: EditorInput; + readonly isNew: boolean; } export interface ISerializedEditorInput { - id: string; - value: string; + readonly id: string; + readonly value: string; } export interface ISerializedEditorGroup { - id: number; - editors: ISerializedEditorInput[]; - mru: number[]; - preview?: number; + readonly id: number; + readonly editors: ISerializedEditorInput[]; + readonly mru: number[]; + readonly preview?: number; sticky?: number; } @@ -174,7 +179,7 @@ export class EditorGroup extends Disposable { return this.preview; } - openEditor(candidate: EditorInput, options?: IEditorOpenOptions): EditorInput { + openEditor(candidate: EditorInput, options?: IEditorOpenOptions): IEditorOpenResult { const makeSticky = options?.sticky || (typeof options?.index === 'number' && this.isSticky(options.index)); const makePinned = options?.pinned || options?.sticky; const makeActive = options?.active || !this.activeEditor || (!makePinned && this.matches(this.preview, this.activeEditor)); @@ -274,7 +279,10 @@ export class EditorGroup extends Disposable { this.doSetActive(newEditor); } - return newEditor; + return { + editor: newEditor, + isNew: true + }; } // Existing editor @@ -302,7 +310,10 @@ export class EditorGroup extends Disposable { this.doStick(existingEditor, this.indexOf(existingEditor)); } - return existingEditor; + return { + editor: existingEditor, + isNew: false + }; } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 347f08e869c..8e803ec2ab4 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -25,7 +25,7 @@ import { ResolvedKeybinding, KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, IExtension, ExtensionContainers } from 'vs/workbench/contrib/extensions/common/extensions'; import { RatingsWidget, InstallCountWidget, RemoteBadgeWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets'; -import { EditorOptions } from 'vs/workbench/common/editor'; +import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { CombinedInstallAction, UpdateAction, ExtensionEditorDropDownAction, ReloadAction, MaliciousStatusLabelAction, IgnoreExtensionRecommendationAction, UndoIgnoreExtensionRecommendationAction, EnableDropDownAction, DisableDropDownAction, StatusLabelAction, SetFileIconThemeAction, SetColorThemeAction, RemoteInstallAction, ExtensionToolTipAction, SystemDisabledWarningAction, LocalInstallAction, SyncIgnoredIconAction, SetProductIconThemeAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -313,8 +313,8 @@ export class ExtensionEditor extends BaseEditor { return disposables; } - async setInput(input: ExtensionsInput, options: EditorOptions | undefined, token: CancellationToken): Promise { - await super.setInput(input, options, token); + async setInput(input: ExtensionsInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { + await super.setInput(input, options, context, token); if (this.template) { await this.updateTemplate(input, this.template, !!options?.preserveFocus); } diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts index 4beec737735..d40c20d8c66 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts @@ -12,7 +12,7 @@ import { Action } from 'vs/base/common/actions'; import { VIEWLET_ID, TEXT_FILE_EDITOR_ID, IExplorerService } from 'vs/workbench/contrib/files/common/files'; import { ITextFileService, TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles'; import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; -import { EditorOptions, TextEditorOptions, IEditorInput } from 'vs/workbench/common/editor'; +import { EditorOptions, TextEditorOptions, IEditorInput, IEditorOpenContext } from 'vs/workbench/common/editor'; import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -91,13 +91,13 @@ export class TextFileEditor extends BaseTextEditor { return this._input as FileEditorInput; } - async setInput(input: FileEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { + async setInput(input: FileEditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { // Update/clear view settings if input changes this.doSaveOrClearTextEditorViewState(this.input); // Set input and resolve - await super.setInput(input, options, token); + await super.setInput(input, options, context, token); try { const resolvedModel = await input.resolve(); @@ -119,10 +119,12 @@ export class TextFileEditor extends BaseTextEditor { const textEditor = assertIsDefined(this.getControl()); textEditor.setModel(textFileModel.textEditorModel); - // Always restore View State if any associated - const editorViewState = this.loadTextEditorViewState(input.resource); - if (editorViewState) { - textEditor.restoreViewState(editorViewState); + // Always restore View State if any associated and not disabled via settings + if (this.shouldRestoreTextEditorViewState(input, context)) { + const editorViewState = this.loadTextEditorViewState(input.resource); + if (editorViewState) { + textEditor.restoreViewState(editorViewState); + } } // TextOptions (avoiding instanceof here for a reason, do not change!) @@ -242,7 +244,7 @@ export class TextFileEditor extends BaseTextEditor { // If the user configured to not restore view state, we clear the view // state unless the editor is still opened in the group. - if (!this.shouldRestoreViewState && (!this.group || !this.group.isOpened(input))) { + if (!this.shouldRestoreTextEditorViewState(input) && (!this.group || !this.group.isOpened(input))) { this.clearTextEditorViewState([input.resource], this.group); } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index f5390824196..0a42fd6b133 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -16,7 +16,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { EditorOptions, IEditorInput, IEditorMemento } from 'vs/workbench/common/editor'; +import { EditorOptions, IEditorInput, IEditorMemento, IEditorOpenContext } from 'vs/workbench/common/editor'; import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput'; import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; import { IBorrowValue, INotebookEditorWidgetService } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidgetService'; @@ -126,12 +126,12 @@ export class NotebookEditor extends BaseEditor { this._widget.value?.focus(); } - async setInput(input: NotebookEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { + async setInput(input: NotebookEditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { const group = this.group!; this._saveEditorViewState(this.input); - await super.setInput(input, options, token); + await super.setInput(input, options, context, token); // Check for cancellation if (token.isCancellationRequested) { diff --git a/src/vs/workbench/contrib/output/browser/outputView.ts b/src/vs/workbench/contrib/output/browser/outputView.ts index 2e5bd2cc7eb..337746000d6 100644 --- a/src/vs/workbench/contrib/output/browser/outputView.ts +++ b/src/vs/workbench/contrib/output/browser/outputView.ts @@ -13,7 +13,7 @@ import { ITextResourceConfigurationService } from 'vs/editor/common/services/tex import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; +import { EditorInput, EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; import { AbstractTextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor'; import { OUTPUT_VIEW_ID, IOutputService, CONTEXT_IN_OUTPUT, IOutputChannel, CONTEXT_ACTIVE_LOG_OUTPUT, CONTEXT_OUTPUT_SCROLL_LOCK } from 'vs/workbench/contrib/output/common/output'; import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; @@ -146,7 +146,7 @@ export class OutputViewPane extends ViewPane { this.channelId = channel.id; const descriptor = this.outputService.getChannelDescriptor(channel.id); CONTEXT_ACTIVE_LOG_OUTPUT.bindTo(this.contextKeyService).set(!!descriptor?.file && descriptor?.log); - this.editorPromise = this.editor.setInput(this.createInput(channel), EditorOptions.create({ preserveFocus: true }), CancellationToken.None) + this.editorPromise = this.editor.setInput(this.createInput(channel), EditorOptions.create({ preserveFocus: true }), Object.create(null), CancellationToken.None) .then(() => this.editor); } @@ -228,7 +228,7 @@ export class OutputEditor extends AbstractTextResourceEditor { return channel ? nls.localize('outputViewWithInputAriaLabel', "{0}, Output panel", channel.label) : nls.localize('outputViewAriaLabel', "Output panel"); } - async setInput(input: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { + async setInput(input: EditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { const focus = !(options && options.preserveFocus); if (input.matches(this.input)) { return; @@ -238,7 +238,7 @@ export class OutputEditor extends AbstractTextResourceEditor { // Dispose previous input (Output panel is not a workbench editor) this.input.dispose(); } - await super.setInput(input, options, token); + await super.setInput(input, options, context, token); if (focus) { this.focus(); } diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index fc4533b84ea..454f80307f0 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -15,7 +15,7 @@ import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLa import { IAction, Action, Separator } from 'vs/base/common/actions'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { EditorOptions } from 'vs/workbench/common/editor'; +import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { KeybindingsEditorModel, IKeybindingItemEntry, IListEntry, KEYBINDING_ENTRY_TEMPLATE_ID } from 'vs/workbench/services/preferences/common/keybindingsEditorModel'; @@ -138,9 +138,9 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditorP this.createBody(keybindingsEditorElement); } - setInput(input: KeybindingsEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { + setInput(input: KeybindingsEditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { this.keybindingsEditorContextKey.set(true); - return super.setInput(input, options, token) + return super.setInput(input, options, context, token) .then(() => this.render(!!(options && options.preserveFocus))); } diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts index b0401d43adc..7a74977c5d8 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts @@ -42,7 +42,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor'; 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 { EditorInput, EditorOptions, IEditorControl, IEditorOpenContext } from 'vs/workbench/common/editor'; import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; import { DefaultSettingsRenderer, FolderSettingsRenderer, IPreferencesRenderer, UserSettingsRenderer, WorkspaceSettingsRenderer } from 'vs/workbench/contrib/preferences/browser/preferencesRenderers'; import { SearchWidget, SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; @@ -151,14 +151,14 @@ export class PreferencesEditor extends BaseEditor { this.preferencesRenderers.editFocusedPreference(); } - setInput(newInput: EditorInput, options: SettingsEditorOptions | undefined, token: CancellationToken): Promise { + setInput(newInput: EditorInput, options: SettingsEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { this.defaultSettingsEditorContextKey.set(true); this.defaultSettingsJSONEditorContextKey.set(true); if (options && options.query) { this.focusSearch(options.query); } - return super.setInput(newInput, options, token).then(() => this.updateInput(newInput as PreferencesEditorInput, options, token)); + return super.setInput(newInput, options, context, token).then(() => this.updateInput(newInput as PreferencesEditorInput, options, context, token)); } layout(dimension: DOM.Dimension): void { @@ -204,8 +204,8 @@ export class PreferencesEditor extends BaseEditor { super.setEditorVisible(visible, group); } - private updateInput(newInput: PreferencesEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { - return this.sideBySidePreferencesWidget.setInput(newInput.secondary, newInput.primary, options, token).then(({ defaultPreferencesRenderer, editablePreferencesRenderer }) => { + private updateInput(newInput: PreferencesEditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { + return this.sideBySidePreferencesWidget.setInput(newInput.secondary, newInput.primary, options, context, token).then(({ defaultPreferencesRenderer, editablePreferencesRenderer }) => { if (token.isCancellationRequested) { return; } @@ -837,12 +837,12 @@ class SideBySidePreferencesWidget extends Widget { this._register(focusTracker.onDidFocus(() => this._onFocus.fire())); } - setInput(defaultPreferencesEditorInput: DefaultPreferencesEditorInput, editablePreferencesEditorInput: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise<{ defaultPreferencesRenderer?: IPreferencesRenderer, editablePreferencesRenderer?: IPreferencesRenderer; }> { + setInput(defaultPreferencesEditorInput: DefaultPreferencesEditorInput, editablePreferencesEditorInput: EditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise<{ defaultPreferencesRenderer?: IPreferencesRenderer, editablePreferencesRenderer?: IPreferencesRenderer; }> { this.getOrCreateEditablePreferencesEditor(editablePreferencesEditorInput); this.settingsTargetsWidget.settingsTarget = this.getSettingsTarget(editablePreferencesEditorInput.resource!); return Promise.all([ - this.updateInput(this.defaultPreferencesEditor, defaultPreferencesEditorInput, DefaultSettingsEditorContribution.ID, editablePreferencesEditorInput.resource!, options, token), - this.updateInput(this.editablePreferencesEditor!, editablePreferencesEditorInput, SettingsEditorContribution.ID, defaultPreferencesEditorInput.resource!, options, token) + this.updateInput(this.defaultPreferencesEditor, defaultPreferencesEditorInput, DefaultSettingsEditorContribution.ID, editablePreferencesEditorInput.resource!, options, context, token), + this.updateInput(this.editablePreferencesEditor!, editablePreferencesEditorInput, SettingsEditorContribution.ID, defaultPreferencesEditorInput.resource!, options, context, token) ]) .then(([defaultPreferencesRenderer, editablePreferencesRenderer]) => { if (token.isCancellationRequested) { @@ -920,8 +920,8 @@ class SideBySidePreferencesWidget extends Widget { return editor; } - private updateInput(editor: BaseEditor, input: EditorInput, editorContributionId: string, associatedPreferencesModelUri: URI, options: EditorOptions | undefined, token: CancellationToken): Promise | undefined> { - return editor.setInput(input, options, token) + private updateInput(editor: BaseEditor, input: EditorInput, editorContributionId: string, associatedPreferencesModelUri: URI, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise | undefined> { + return editor.setInput(input, options, context, token) .then(() => { if (token.isCancellationRequested) { return undefined; @@ -1025,8 +1025,8 @@ export class DefaultPreferencesEditor extends BaseTextEditor { return options; } - setInput(input: DefaultPreferencesEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { - return super.setInput(input, options, token) + setInput(input: DefaultPreferencesEditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { + return super.setInput(input, options, context, token) .then(() => this.input!.resolve() .then(editorModel => { if (token.isCancellationRequested) { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index fff8e591148..870949d9c61 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -40,7 +40,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { IUserDataAutoSyncService, IUserDataSyncService, SyncStatus } from 'vs/platform/userDataSync/common/userDataSync'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { IEditorMemento, IEditorPane } from 'vs/workbench/common/editor'; +import { IEditorMemento, IEditorOpenContext, IEditorPane } from 'vs/workbench/common/editor'; import { attachSuggestEnabledInputBoxStyler, SuggestEnabledInput } from 'vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput'; import { SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; import { commonlyUsedData, tocData } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; @@ -235,9 +235,9 @@ export class SettingsEditor2 extends BaseEditor { this.updateStyles(); } - setInput(input: SettingsEditor2Input, options: SettingsEditorOptions | undefined, token: CancellationToken): Promise { + setInput(input: SettingsEditor2Input, options: SettingsEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { this.inSettingsEditorContextKey.set(true); - return super.setInput(input, options, token) + return super.setInput(input, options, context, token) .then(() => timeout(0)) // Force setInput to be async .then(() => { // Don't block setInput on render (which can trigger an async search) diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts index 1254e119d01..4c1c7205324 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts @@ -37,7 +37,7 @@ import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; -import { EditorOptions } from 'vs/workbench/common/editor'; +import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; import { ExcludePatternInputWidget, PatternInputWidget } from 'vs/workbench/contrib/search/browser/patternInputWidget'; import { SearchWidget } from 'vs/workbench/contrib/search/browser/searchWidget'; import { InputBoxFocusedKey } from 'vs/workbench/contrib/search/common/constants'; @@ -556,10 +556,10 @@ export class SearchEditor extends BaseTextEditor { return this._input as SearchEditorInput; } - async setInput(newInput: SearchEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { + async setInput(newInput: SearchEditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { this.saveViewState(); - await super.setInput(newInput, options, token); + await super.setInput(newInput, options, context, token); if (token.isCancellationRequested) { return; } const { body, config } = await newInput.getModels(); diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts index 2b4b56fa148..a3c43f73982 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts @@ -13,7 +13,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { IEditorDropService } from 'vs/workbench/services/editor/browser/editorDropService'; -import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; +import { EditorInput, EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; import { WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -107,7 +107,7 @@ export class WebviewEditor extends BaseEditor { super.clearInput(); } - public async setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Promise { + public async setInput(input: EditorInput, options: EditorOptions, context: IEditorOpenContext, token: CancellationToken): Promise { if (input.matches(this.input)) { return; } @@ -117,7 +117,7 @@ export class WebviewEditor extends BaseEditor { this.webview.release(this); } - await super.setInput(input, options, token); + await super.setInput(input, options, context, token); await input.resolve(); if (token.isCancellationRequested) { diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts index 4d73d5db9cf..b8e307a3609 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts @@ -10,7 +10,7 @@ import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { EditorOptions, IEditorMemento } from 'vs/workbench/common/editor'; +import { EditorOptions, IEditorMemento, IEditorOpenContext } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput'; @@ -262,7 +262,7 @@ export class WalkThroughPart extends BaseEditor { this.scrollbar.setScrollPosition({ scrollTop: scrollPosition.scrollTop + scrollDimensions.height }); } - setInput(input: WalkThroughInput, options: EditorOptions | undefined, token: CancellationToken): Promise { + setInput(input: WalkThroughInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { if (this.input instanceof WalkThroughInput) { this.saveTextEditorViewState(this.input); } @@ -270,7 +270,7 @@ export class WalkThroughPart extends BaseEditor { this.contentDisposables = dispose(this.contentDisposables); this.content.innerText = ''; - return super.setInput(input, options, token) + return super.setInput(input, options, context, token) .then(() => { return input.resolve(); }) diff --git a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts index 82a0abe7d2a..2255e8648e7 100644 --- a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts @@ -106,7 +106,7 @@ suite('Workbench base editor', () => { assert(!e.isVisible()); assert(!e.input); - await e.setInput(input, options, CancellationToken.None); + await e.setInput(input, options, Object.create(null), CancellationToken.None); assert.strictEqual(input, e.input); const group = new TestEditorGroupView(1); e.setVisible(true, group); diff --git a/src/vs/workbench/test/browser/parts/editor/editorGroups.test.ts b/src/vs/workbench/test/browser/parts/editor/editorGroups.test.ts index 1e618eb1943..5c399799eaf 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorGroups.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorGroups.test.ts @@ -464,8 +464,9 @@ suite('Workbench editor groups', () => { // Active && Pinned const input1 = input(); - const openedEditor = group.openEditor(input1, { active: true, pinned: true }); + const { editor: openedEditor, isNew } = group.openEditor(input1, { active: true, pinned: true }); assert.equal(openedEditor, input1); + assert.equal(isNew, true); assert.equal(group.count, 1); assert.equal(group.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE).length, 1); @@ -575,11 +576,13 @@ suite('Workbench editor groups', () => { const input3 = input('3'); // Pinned and Active - let openedEditor = group.openEditor(input1, { pinned: true, active: true }); - assert.equal(openedEditor, input1); + let openedEditorResult = group.openEditor(input1, { pinned: true, active: true }); + assert.equal(openedEditorResult.editor, input1); + assert.equal(openedEditorResult.isNew, true); - openedEditor = group.openEditor(input1Copy, { pinned: true, active: true }); // opening copy of editor should still return existing one - assert.equal(openedEditor, input1); + openedEditorResult = group.openEditor(input1Copy, { pinned: true, active: true }); // opening copy of editor should still return existing one + assert.equal(openedEditorResult.editor, input1); + assert.equal(openedEditorResult.isNew, false); group.openEditor(input2, { pinned: true, active: true }); group.openEditor(input3, { pinned: true, active: true }); @@ -1145,7 +1148,7 @@ suite('Workbench editor groups', () => { // [] -> /index.html/ const indexHtml = input('index.html'); - let openedEditor = group.openEditor(indexHtml); + let openedEditor = group.openEditor(indexHtml).editor; assert.equal(openedEditor, indexHtml); assert.equal(group.activeEditor, indexHtml); assert.equal(group.previewEditor, indexHtml); @@ -1154,7 +1157,7 @@ suite('Workbench editor groups', () => { // /index.html/ -> /index.html/ const sameIndexHtml = input('index.html'); - openedEditor = group.openEditor(sameIndexHtml); + openedEditor = group.openEditor(sameIndexHtml).editor; assert.equal(openedEditor, indexHtml); assert.equal(group.activeEditor, indexHtml); assert.equal(group.previewEditor, indexHtml); @@ -1163,7 +1166,7 @@ suite('Workbench editor groups', () => { // /index.html/ -> /style.css/ const styleCss = input('style.css'); - openedEditor = group.openEditor(styleCss); + openedEditor = group.openEditor(styleCss).editor; assert.equal(openedEditor, styleCss); assert.equal(group.activeEditor, styleCss); assert.equal(group.previewEditor, styleCss); @@ -1172,7 +1175,7 @@ suite('Workbench editor groups', () => { // /style.css/ -> [/style.css/, test.js] const testJs = input('test.js'); - openedEditor = group.openEditor(testJs, { active: true, pinned: true }); + openedEditor = group.openEditor(testJs, { active: true, pinned: true }).editor; assert.equal(openedEditor, testJs); assert.equal(group.previewEditor, styleCss); assert.equal(group.activeEditor, testJs); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 8840867e739..c162c3e0320 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -10,7 +10,7 @@ import * as resources from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import { IEditorInputWithOptions, IEditorIdentifier, IUntitledTextResourceEditorInput, IResourceDiffEditorInput, IEditorInput, IEditorPane, IEditorCloseEvent, IEditorPartOptions, IRevertOptions, GroupIdentifier, EditorInput, EditorOptions, EditorsOrder, IFileEditorInput, IEditorInputFactoryRegistry, IEditorInputFactory, Extensions as EditorExtensions, ISaveOptions, IMoveResult, ITextEditorPane, ITextDiffEditorPane, IVisibleEditorPane } from 'vs/workbench/common/editor'; +import { IEditorInputWithOptions, IEditorIdentifier, IUntitledTextResourceEditorInput, IResourceDiffEditorInput, IEditorInput, IEditorPane, IEditorCloseEvent, IEditorPartOptions, IRevertOptions, GroupIdentifier, EditorInput, EditorOptions, EditorsOrder, IFileEditorInput, IEditorInputFactoryRegistry, IEditorInputFactory, Extensions as EditorExtensions, ISaveOptions, IMoveResult, ITextEditorPane, ITextDiffEditorPane, IVisibleEditorPane, IEditorOpenContext } from 'vs/workbench/common/editor'; import { IEditorOpeningEvent, EditorServiceImpl, IEditorGroupView, IEditorGroupsAccessor } from 'vs/workbench/browser/parts/editor/editor'; import { Event, Emitter } from 'vs/base/common/event'; import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup'; @@ -1072,8 +1072,8 @@ export function registerTestEditor(id: string, inputs: SyncDescriptor { - super.setInput(input, options, token); + async setInput(input: EditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { + super.setInput(input, options, context, token); await input.resolve(); } From 40164f1f5b521795a29fd488454b8f5dbf54051d Mon Sep 17 00:00:00 2001 From: mtaran-google Date: Sun, 23 Aug 2020 23:14:40 -0700 Subject: [PATCH 449/736] Add to WorkingCopyCapabilities enum to reflect usage. (#105020) * Add to WorkingCopyCapabilities enum to reflect usage. The capabilities field was being used with a value of 0 in a few places that implemented the IWorkingCopy interface. This inconsistency wasn't picked up by the compiler because the value in the enum was specified as `1 << 1` and not as `2`. This changes the value to be specified as `2` (for better type checking) and makes the 0 to use the new enum value. * some polish on top Co-authored-by: Maksym Taran Co-authored-by: Benjamin Pasero --- src/vs/workbench/api/browser/mainThreadCustomEditors.ts | 2 +- .../workbench/contrib/notebook/common/notebookEditorModel.ts | 2 +- .../contrib/searchEditor/browser/searchEditorInput.ts | 2 +- .../services/textfile/common/textFileEditorModel.ts | 4 ++-- .../services/workingCopy/common/workingCopyService.ts | 5 +++++ .../workingCopy/test/common/workingCopyService.test.ts | 4 ++-- 6 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts index 620922edc5e..848f4a142ed 100644 --- a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts @@ -333,7 +333,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod } public get capabilities(): WorkingCopyCapabilities { - return 0; + return WorkingCopyCapabilities.None; } public isDirty(): boolean { diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts index a0e064a2aec..9ef0ab0a5ff 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts @@ -84,7 +84,7 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN this._register(this._workingCopyService.registerWorkingCopy(workingCopyAdapter)); } - capabilities = 0; + capabilities = WorkingCopyCapabilities.None; async backup(): Promise> { if (this._notebook.supportBackup) { diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts index c884a84be2b..da6858a833e 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts @@ -112,7 +112,7 @@ export class SearchEditorInput extends EditorInput { const workingCopyAdapter = new class implements IWorkingCopy { readonly resource = input.modelUri; get name() { return input.getName(); } - readonly capabilities = input.isUntitled() ? WorkingCopyCapabilities.Untitled : 0; + readonly capabilities = input.isUntitled() ? WorkingCopyCapabilities.Untitled : WorkingCopyCapabilities.None; readonly onDidChangeDirty = input.onDidChangeDirty; readonly onDidChangeContent = input.onDidChangeContent; isDirty(): boolean { return input.isDirty(); } diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 1bb7ac6975d..721d459f8d4 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -19,7 +19,7 @@ import { ITextBufferFactory, ITextModel } from 'vs/editor/common/model'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ILogService } from 'vs/platform/log/common/log'; import { basename } from 'vs/base/common/path'; -import { IWorkingCopyService, IWorkingCopyBackup } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { IWorkingCopyService, IWorkingCopyBackup, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { ILabelService } from 'vs/platform/label/common/label'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; @@ -65,7 +65,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil //#endregion - readonly capabilities = 0; + readonly capabilities = WorkingCopyCapabilities.None; readonly name = basename(this.labelService.getUriLabel(this.resource)); diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts index ba56c701fec..e5f96509de8 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts @@ -14,6 +14,11 @@ import { ITextSnapshot } from 'vs/editor/common/model'; export const enum WorkingCopyCapabilities { + /** + * Signals no specific capability for the working copy. + */ + None = 0, + /** * Signals that the working copy requires * additional input when saving, e.g. an diff --git a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts index 50fbeff1806..97840f97145 100644 --- a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IWorkingCopy, IWorkingCopyBackup } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { IWorkingCopy, IWorkingCopyBackup, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { URI } from 'vs/base/common/uri'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -23,7 +23,7 @@ export class TestWorkingCopy extends Disposable implements IWorkingCopy { private readonly _onDispose = this._register(new Emitter()); readonly onDispose = this._onDispose.event; - readonly capabilities = 0; + readonly capabilities = WorkingCopyCapabilities.None; readonly name = basename(this.resource); From 4dcc6b59c7d453a47f3322564c877f265b10bf66 Mon Sep 17 00:00:00 2001 From: Rotem B <10165140+rotem-bar@users.noreply.github.com> Date: Mon, 24 Aug 2020 09:22:22 +0300 Subject: [PATCH 450/736] fix(extpath): windows reserved names being allowed with file extension (#104980) Co-authored-by: Rotem B <10165140+Jensui@users.noreply.github.com> --- src/vs/base/common/extpath.ts | 2 +- src/vs/base/test/common/extpath.test.ts | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/vs/base/common/extpath.ts b/src/vs/base/common/extpath.ts index d6e7d6ef5d7..05d343a5e1b 100644 --- a/src/vs/base/common/extpath.ts +++ b/src/vs/base/common/extpath.ts @@ -142,7 +142,7 @@ export function isUNC(path: string): boolean { // Reference: https://en.wikipedia.org/wiki/Filename const WINDOWS_INVALID_FILE_CHARS = /[\\/:\*\?"<>\|]/g; const UNIX_INVALID_FILE_CHARS = /[\\/]/g; -const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])$/i; +const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])(\.(.*?))?$/i; export function isValidBasename(name: string | null | undefined, isWindowsOS: boolean = isWindows): boolean { const invalidFileChars = isWindowsOS ? WINDOWS_INVALID_FILE_CHARS : UNIX_INVALID_FILE_CHARS; diff --git a/src/vs/base/test/common/extpath.test.ts b/src/vs/base/test/common/extpath.test.ts index 0760e2c8b91..03993437ffc 100644 --- a/src/vs/base/test/common/extpath.test.ts +++ b/src/vs/base/test/common/extpath.test.ts @@ -57,6 +57,13 @@ suite('Paths', () => { assert.ok(!extpath.isValidBasename('aux')); assert.ok(!extpath.isValidBasename('Aux')); assert.ok(!extpath.isValidBasename('LPT0')); + assert.ok(!extpath.isValidBasename('aux.txt')); + assert.ok(!extpath.isValidBasename('com0.abc')); + assert.ok(extpath.isValidBasename('LPT00')); + assert.ok(extpath.isValidBasename('aux1')); + assert.ok(extpath.isValidBasename('aux1.txt')); + assert.ok(extpath.isValidBasename('aux1.aux.txt')); + assert.ok(!extpath.isValidBasename('test.txt.')); assert.ok(!extpath.isValidBasename('test.txt..')); assert.ok(!extpath.isValidBasename('test.txt ')); From f44d46d68233b3da5821554456d08f28e4efe841 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 24 Aug 2020 08:30:15 +0200 Subject: [PATCH 451/736] editors - baseEditor => editorPane --- src/vs/workbench/browser/editor.ts | 15 ++++++++------- .../browser/parts/editor/binaryEditor.ts | 4 ++-- .../browser/parts/editor/editorControl.ts | 18 +++++++++--------- .../editor/{baseEditor.ts => editorPane.ts} | 8 ++++---- .../browser/parts/editor/sideBySideEditor.ts | 14 +++++++------- .../browser/parts/editor/textEditor.ts | 4 ++-- .../browser/parts/editor/titleControl.ts | 6 +++--- src/vs/workbench/common/editor.ts | 2 +- .../extensions/browser/extensionEditor.ts | 4 ++-- .../runtimeExtensionsEditor.ts | 4 ++-- .../contrib/notebook/browser/notebookEditor.ts | 6 +++--- .../notebook/browser/notebookEditorWidget.ts | 2 +- .../preferences/browser/keybindingsEditor.ts | 4 ++-- .../preferences/browser/preferencesEditor.ts | 12 ++++++------ .../preferences/browser/settingsEditor2.ts | 6 +++--- .../contrib/webview/browser/webviewEditor.ts | 4 ++-- .../walkThrough/browser/walkThroughPart.ts | 4 ++-- .../editor/test/browser/editorService.test.ts | 4 ++-- .../{baseEditor.test.ts => editorPane.test.ts} | 10 +++++----- .../test/browser/workbenchTestServices.ts | 6 +++--- 20 files changed, 69 insertions(+), 68 deletions(-) rename src/vs/workbench/browser/parts/editor/{baseEditor.ts => editorPane.ts} (98%) rename src/vs/workbench/test/browser/parts/editor/{baseEditor.test.ts => editorPane.test.ts} (97%) diff --git a/src/vs/workbench/browser/editor.ts b/src/vs/workbench/browser/editor.ts index 8e0aec443eb..5df3e69fd8d 100644 --- a/src/vs/workbench/browser/editor.ts +++ b/src/vs/workbench/browser/editor.ts @@ -6,17 +6,18 @@ import { EditorInput } from 'vs/workbench/common/editor'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { Registry } from 'vs/platform/registry/common/platform'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IConstructorSignature0, IInstantiationService, BrandedService } from 'vs/platform/instantiation/common/instantiation'; import { insert } from 'vs/base/common/arrays'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; export interface IEditorDescriptor { - instantiate(instantiationService: IInstantiationService): BaseEditor; getId(): string; getName(): string; + instantiate(instantiationService: IInstantiationService): EditorPane; + describes(obj: unknown): boolean; } @@ -56,20 +57,20 @@ export interface IEditorRegistry { export class EditorDescriptor implements IEditorDescriptor { static create( - ctor: { new(...services: Services): BaseEditor }, + ctor: { new(...services: Services): EditorPane }, id: string, name: string ): EditorDescriptor { - return new EditorDescriptor(ctor as IConstructorSignature0, id, name); + return new EditorDescriptor(ctor as IConstructorSignature0, id, name); } constructor( - private readonly ctor: IConstructorSignature0, + private readonly ctor: IConstructorSignature0, private readonly id: string, private readonly name: string ) { } - instantiate(instantiationService: IInstantiationService): BaseEditor { + instantiate(instantiationService: IInstantiationService): EditorPane { return instantiationService.createInstance(this.ctor); } @@ -82,7 +83,7 @@ export class EditorDescriptor implements IEditorDescriptor { } describes(obj: unknown): boolean { - return obj instanceof BaseEditor && obj.getId() === this.id; + return obj instanceof EditorPane && obj.getId() === this.id; } } diff --git a/src/vs/workbench/browser/parts/editor/binaryEditor.ts b/src/vs/workbench/browser/parts/editor/binaryEditor.ts index c04fdac81bb..bed01dd0687 100644 --- a/src/vs/workbench/browser/parts/editor/binaryEditor.ts +++ b/src/vs/workbench/browser/parts/editor/binaryEditor.ts @@ -7,7 +7,7 @@ import 'vs/css!./media/binaryeditor'; import * as nls from 'vs/nls'; import { Emitter } from 'vs/base/common/event'; import { EditorInput, EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; @@ -30,7 +30,7 @@ export interface IOpenCallbacks { /* * This class is only intended to be subclassed and not instantiated. */ -export abstract class BaseBinaryResourceEditor extends BaseEditor { +export abstract class BaseBinaryResourceEditor extends EditorPane { private readonly _onMetadataChanged = this._register(new Emitter()); readonly onMetadataChanged = this._onMetadataChanged.event; diff --git a/src/vs/workbench/browser/parts/editor/editorControl.ts b/src/vs/workbench/browser/parts/editor/editorControl.ts index 315c26376f9..5c27e74e8d2 100644 --- a/src/vs/workbench/browser/parts/editor/editorControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorControl.ts @@ -9,7 +9,7 @@ import { Dimension, show, hide, addClass } from 'vs/base/browser/dom'; import { Registry } from 'vs/platform/registry/common/platform'; import { IEditorRegistry, Extensions as EditorExtensions, IEditorDescriptor } from 'vs/workbench/browser/editor'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorProgressService, LongRunningOperation } from 'vs/platform/progress/common/progress'; import { IEditorGroupView, DEFAULT_EDITOR_MIN_DIMENSIONS, DEFAULT_EDITOR_MAX_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor'; @@ -17,7 +17,7 @@ import { Emitter } from 'vs/base/common/event'; import { assertIsDefined } from 'vs/base/common/types'; export interface IOpenEditorResult { - readonly editorPane: BaseEditor; + readonly editorPane: EditorPane; readonly editorChanged: boolean; } @@ -34,10 +34,10 @@ export class EditorControl extends Disposable { private _onDidSizeConstraintsChange = this._register(new Emitter<{ width: number; height: number; } | undefined>()); readonly onDidSizeConstraintsChange = this._onDidSizeConstraintsChange.event; - private _activeEditorPane: BaseEditor | null = null; + private _activeEditorPane: EditorPane | null = null; get activeEditorPane(): IVisibleEditorPane | null { return this._activeEditorPane as IVisibleEditorPane | null; } - private readonly editorPanes: BaseEditor[] = []; + private readonly editorPanes: EditorPane[] = []; private readonly activeEditorPaneDisposables = this._register(new DisposableStore()); private dimension: Dimension | undefined; @@ -67,7 +67,7 @@ export class EditorControl extends Disposable { return { editorPane, editorChanged }; } - private doShowEditorPane(descriptor: IEditorDescriptor): BaseEditor { + private doShowEditorPane(descriptor: IEditorDescriptor): EditorPane { // Return early if the currently active editor pane can handle the input if (this._activeEditorPane && descriptor.describes(this._activeEditorPane)) { @@ -99,7 +99,7 @@ export class EditorControl extends Disposable { return editorPane; } - private doCreateEditorPane(descriptor: IEditorDescriptor): BaseEditor { + private doCreateEditorPane(descriptor: IEditorDescriptor): EditorPane { // Instantiate editor const editorPane = this.doInstantiateEditorPane(descriptor); @@ -116,7 +116,7 @@ export class EditorControl extends Disposable { return editorPane; } - private doInstantiateEditorPane(descriptor: IEditorDescriptor): BaseEditor { + private doInstantiateEditorPane(descriptor: IEditorDescriptor): EditorPane { // Return early if already instantiated const existingEditorPane = this.editorPanes.find(editorPane => descriptor.describes(editorPane)); @@ -131,7 +131,7 @@ export class EditorControl extends Disposable { return editorPane; } - private doSetActiveEditorPane(editorPane: BaseEditor | null) { + private doSetActiveEditorPane(editorPane: EditorPane | null) { this._activeEditorPane = editorPane; // Clear out previous active editor pane listeners @@ -147,7 +147,7 @@ export class EditorControl extends Disposable { this._onDidSizeConstraintsChange.fire(undefined); } - private async doSetInput(editorPane: BaseEditor, editor: EditorInput, options: EditorOptions | undefined, context: IEditorOpenContext): Promise { + private async doSetInput(editorPane: EditorPane, editor: EditorInput, options: EditorOptions | undefined, context: IEditorOpenContext): Promise { // If the input did not change, return early and only apply the options // unless the options instruct us to force open it even if it is the same diff --git a/src/vs/workbench/browser/parts/editor/baseEditor.ts b/src/vs/workbench/browser/parts/editor/editorPane.ts similarity index 98% rename from src/vs/workbench/browser/parts/editor/baseEditor.ts rename to src/vs/workbench/browser/parts/editor/editorPane.ts index b3cce045b9a..e5e48349b04 100644 --- a/src/vs/workbench/browser/parts/editor/baseEditor.ts +++ b/src/vs/workbench/browser/parts/editor/editorPane.ts @@ -41,7 +41,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; * * This class is only intended to be subclassed and not instantiated. */ -export abstract class BaseEditor extends Composite implements IEditorPane { +export abstract class EditorPane extends Composite implements IEditorPane { private static readonly EDITOR_MEMENTOS = new Map>(); @@ -148,10 +148,10 @@ export abstract class BaseEditor extends Composite implements IEditorPane { protected getEditorMemento(editorGroupService: IEditorGroupsService, key: string, limit: number = 10): IEditorMemento { const mementoKey = `${this.getId()}${key}`; - let editorMemento = BaseEditor.EDITOR_MEMENTOS.get(mementoKey); + let editorMemento = EditorPane.EDITOR_MEMENTOS.get(mementoKey); if (!editorMemento) { editorMemento = new EditorMemento(this.getId(), key, this.getMemento(StorageScope.WORKSPACE), limit, editorGroupService); - BaseEditor.EDITOR_MEMENTOS.set(mementoKey, editorMemento); + EditorPane.EDITOR_MEMENTOS.set(mementoKey, editorMemento); } return editorMemento; @@ -160,7 +160,7 @@ export abstract class BaseEditor extends Composite implements IEditorPane { protected saveState(): void { // Save all editor memento for this editor type - BaseEditor.EDITOR_MEMENTOS.forEach(editorMemento => { + EditorPane.EDITOR_MEMENTOS.forEach(editorMemento => { if (editorMemento.id === this.getId()) { editorMemento.saveState(); } diff --git a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts index e0a8f911033..ed99e7e0567 100644 --- a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts +++ b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts @@ -6,7 +6,7 @@ import * as DOM from 'vs/base/browser/dom'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorInput, EditorOptions, SideBySideEditorInput, IEditorControl, IEditorPane, IEditorOpenContext } from 'vs/workbench/common/editor'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -19,7 +19,7 @@ import { Event, Relay, Emitter } from 'vs/base/common/event'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { assertIsDefined } from 'vs/base/common/types'; -export class SideBySideEditor extends BaseEditor { +export class SideBySideEditor extends EditorPane { static readonly ID: string = 'workbench.editor.sidebysideEditor'; @@ -33,7 +33,7 @@ export class SideBySideEditor extends BaseEditor { private get minimumSecondaryHeight() { return this.secondaryEditorPane ? this.secondaryEditorPane.minimumHeight : 0; } private get maximumSecondaryHeight() { return this.secondaryEditorPane ? this.secondaryEditorPane.maximumHeight : Number.POSITIVE_INFINITY; } - // these setters need to exist because this extends from BaseEditor + // these setters need to exist because this extends from EditorPane set minimumWidth(value: number) { /* noop */ } set maximumWidth(value: number) { /* noop */ } set minimumHeight(value: number) { /* noop */ } @@ -44,8 +44,8 @@ export class SideBySideEditor extends BaseEditor { get minimumHeight() { return this.minimumPrimaryHeight + this.minimumSecondaryHeight; } get maximumHeight() { return this.maximumPrimaryHeight + this.maximumSecondaryHeight; } - protected primaryEditorPane?: BaseEditor; - protected secondaryEditorPane?: BaseEditor; + protected primaryEditorPane?: EditorPane; + protected secondaryEditorPane?: EditorPane; private primaryEditorContainer: HTMLElement | undefined; private secondaryEditorContainer: HTMLElement | undefined; @@ -188,7 +188,7 @@ export class SideBySideEditor extends BaseEditor { return this.onEditorsCreated(secondaryEditor, primaryEditor, newInput.secondary, newInput.primary, options, context, token); } - private doCreateEditor(editorInput: EditorInput, container: HTMLElement): BaseEditor { + private doCreateEditor(editorInput: EditorInput, container: HTMLElement): EditorPane { const descriptor = Registry.as(EditorExtensions.Editors).getEditor(editorInput); if (!descriptor) { throw new Error('No descriptor for editor found'); @@ -201,7 +201,7 @@ export class SideBySideEditor extends BaseEditor { return editor; } - private async onEditorsCreated(secondary: BaseEditor, primary: BaseEditor, secondaryInput: EditorInput, primaryInput: EditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { + private async onEditorsCreated(secondary: EditorPane, primary: EditorPane, secondaryInput: EditorInput, primaryInput: EditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { this.secondaryEditorPane = secondary; this.primaryEditorPane = primary; diff --git a/src/vs/workbench/browser/parts/editor/textEditor.ts b/src/vs/workbench/browser/parts/editor/textEditor.ts index 429b3125d23..f420982dd27 100644 --- a/src/vs/workbench/browser/parts/editor/textEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textEditor.ts @@ -11,7 +11,7 @@ import { isObject, assertIsDefined, withNullAsUndefined, isFunction } from 'vs/b import { Dimension } from 'vs/base/browser/dom'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { EditorInput, EditorOptions, IEditorMemento, ITextEditorPane, TextEditorOptions, IEditorCloseEvent, IEditorInput, computeEditorAriaLabel, IEditorOpenContext, toResource, SideBySideEditor } from 'vs/workbench/common/editor'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IEditorViewState, IEditor, ScrollType } from 'vs/editor/common/editorCommon'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -35,7 +35,7 @@ export interface IEditorConfiguration { * The base class of editors that leverage the text editor for the editing experience. This class is only intended to * be subclassed and not instantiated. */ -export abstract class BaseTextEditor extends BaseEditor implements ITextEditorPane { +export abstract class BaseTextEditor extends EditorPane implements ITextEditorPane { static readonly TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'textEditorViewState'; diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index c693429a87d..96ba8faeb84 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -28,7 +28,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { listActiveSelectionBackground, listActiveSelectionForeground } from 'vs/platform/theme/common/colorRegistry'; import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant, Themable } from 'vs/platform/theme/common/themeService'; import { DraggedEditorGroupIdentifier, DraggedEditorIdentifier, fillResourceDataTransfers, LocalSelectionTransfer } from 'vs/workbench/browser/dnd'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs'; import { BreadcrumbsControl, IBreadcrumbsControlOptions } from 'vs/workbench/browser/parts/editor/breadcrumbsControl'; import { IEditorGroupsAccessor, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor'; @@ -163,7 +163,7 @@ export abstract class TitleControl extends Themable { const activeEditorPane = this.group.activeEditorPane; // Check Active Editor - if (activeEditorPane instanceof BaseEditor) { + if (activeEditorPane instanceof EditorPane) { const result = activeEditorPane.getActionViewItem(action); if (result) { @@ -236,7 +236,7 @@ export abstract class TitleControl extends Themable { // Editor actions require the editor control to be there, so we retrieve it via service const activeEditorPane = this.group.activeEditorPane; - if (activeEditorPane instanceof BaseEditor) { + if (activeEditorPane instanceof EditorPane) { const codeEditor = getCodeEditor(activeEditorPane.getControl()); const scopedContextKeyService = codeEditor?.invokeWithinContext(accessor => accessor.get(IContextKeyService)) || this.contextKeyService; const titleBarMenu = this.menuService.createMenu(MenuId.EditorTitle, scopedContextKeyService); diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 8792fd3b5f1..d698bbfe734 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -1138,7 +1138,7 @@ export class TextEditorOptions extends EditorOptions implements ITextEditorOptio } /** - * Context passed into `BaseEditor#setInput` to give additional + * Context passed into `EditorPane#setInput` to give additional * context information around why the editor was opened. */ export interface IEditorOpenContext { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 8e803ec2ab4..502a12a626a 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -15,7 +15,7 @@ import { isPromiseCanceledError } from 'vs/base/common/errors'; import { dispose, toDisposable, Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { domEvent } from 'vs/base/browser/event'; import { append, $, addClass, removeClass, finalHandler, join, toggleClass, hide, show, addDisposableListener, EventType } from 'vs/base/browser/dom'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; 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'; @@ -164,7 +164,7 @@ interface IExtensionEditorTemplate { header: HTMLElement; } -export class ExtensionEditor extends BaseEditor { +export class ExtensionEditor extends EditorPane { static readonly ID: string = 'workbench.editor.extension'; diff --git a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts index 1c780f7fb4f..05f5b3e2dd0 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -8,7 +8,7 @@ import * as nls from 'vs/nls'; import * as os from 'os'; import { IProductService } from 'vs/platform/product/common/productService'; import { Action, IAction, Separator } from 'vs/base/common/actions'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionsWorkbenchService, IExtension } from 'vs/workbench/contrib/extensions/common/extensions'; @@ -100,7 +100,7 @@ interface IRuntimeExtension { unresponsiveProfile?: IExtensionHostProfile; } -export class RuntimeExtensionsEditor extends BaseEditor { +export class RuntimeExtensionsEditor extends EditorPane { public static readonly ID: string = 'workbench.editor.runtimeExtensions'; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index 0a42fd6b133..a60746ceea9 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -15,7 +15,7 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/ import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { EditorOptions, IEditorInput, IEditorMemento, IEditorOpenContext } from 'vs/workbench/common/editor'; import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput'; import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; @@ -28,7 +28,7 @@ import { NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/not const NOTEBOOK_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'NotebookEditorViewState'; -export class NotebookEditor extends BaseEditor { +export class NotebookEditor extends EditorPane { static readonly ID: string = 'workbench.editor.notebook'; private readonly _editorMemento: IEditorMemento; @@ -74,7 +74,7 @@ export class NotebookEditor extends BaseEditor { get minimumWidth(): number { return 375; } get maximumWidth(): number { return Number.POSITIVE_INFINITY; } - // these setters need to exist because this extends from BaseEditor + // these setters need to exist because this extends from EditorPane set minimumWidth(value: number) { /*noop*/ } set maximumWidth(value: number) { /*noop*/ } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index d2ad1e4b1b7..e7befc90545 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -24,7 +24,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { contrastBorder, editorBackground, focusBorder, foreground, registerColor, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground, textPreformatForeground, errorForeground, transparent, listFocusBackground, listInactiveSelectionBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, scrollbarSliderActiveBackground } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { EditorMemento } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorMemento } from 'vs/workbench/browser/parts/editor/editorPane'; import { IEditorMemento } from 'vs/workbench/common/editor'; import { CELL_MARGIN, CELL_RUN_GUTTER, CELL_TOP_MARGIN, SCROLLABLE_ELEMENT_PADDING_TOP, BOTTOM_CELL_TOOLBAR_GAP, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, BOTTOM_CELL_TOOLBAR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; import { CellEditState, CellFocusMode, ICellRange, ICellViewModel, INotebookCellList, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_OUTPUT_FOCUSED, INotebookDeltaDecoration, NotebookEditorOptions, INotebookEditorWidgetOptions, INotebookEditorContributionDescription } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index 454f80307f0..fc27aa4c3ba 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -14,7 +14,7 @@ import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlighte import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; import { IAction, Action, Separator } from 'vs/base/common/actions'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; @@ -58,7 +58,7 @@ interface ColumnItem { const oddRowBackgroundColor = new Color(new RGBA(130, 130, 130, 0.04)); -export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditorPane { +export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorPane { static readonly ID: string = 'workbench.editor.keybindings'; diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts index 7a74977c5d8..e8bb2984293 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts @@ -40,7 +40,7 @@ import { attachStylerCallback } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; import { EditorInput, EditorOptions, IEditorControl, IEditorOpenContext } from 'vs/workbench/common/editor'; import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; @@ -53,7 +53,7 @@ import { IFilterResult, IPreferencesService, ISetting, ISettingsEditorModel, ISe import { DefaultPreferencesEditorInput, PreferencesEditorInput } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { DefaultSettingsEditorModel, SettingsEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; -export class PreferencesEditor extends BaseEditor { +export class PreferencesEditor extends EditorPane { static readonly ID: string = 'workbench.editor.preferencesEditor'; @@ -75,7 +75,7 @@ export class PreferencesEditor extends BaseEditor { get minimumWidth(): number { return this.sideBySidePreferencesWidget ? this.sideBySidePreferencesWidget.minimumWidth : 0; } get maximumWidth(): number { return this.sideBySidePreferencesWidget ? this.sideBySidePreferencesWidget.maximumWidth : Number.POSITIVE_INFINITY; } - // these setters need to exist because this extends from BaseEditor + // these setters need to exist because this extends from EditorPane set minimumWidth(value: number) { /*noop*/ } set maximumWidth(value: number) { /*noop*/ } @@ -762,7 +762,7 @@ class SideBySidePreferencesWidget extends Widget { private defaultPreferencesHeader: HTMLElement; private defaultPreferencesEditor: DefaultPreferencesEditor; - private editablePreferencesEditor: BaseEditor | null = null; + private editablePreferencesEditor: EditorPane | null = null; private defaultPreferencesEditorContainer: HTMLElement; private editablePreferencesEditorContainer: HTMLElement; @@ -906,7 +906,7 @@ class SideBySidePreferencesWidget extends Widget { } } - private getOrCreateEditablePreferencesEditor(editorInput: EditorInput): BaseEditor { + private getOrCreateEditablePreferencesEditor(editorInput: EditorInput): EditorPane { if (this.editablePreferencesEditor) { return this.editablePreferencesEditor; } @@ -920,7 +920,7 @@ class SideBySidePreferencesWidget extends Widget { return editor; } - private updateInput(editor: BaseEditor, input: EditorInput, editorContributionId: string, associatedPreferencesModelUri: URI, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise | undefined> { + private updateInput(editor: EditorPane, input: EditorInput, editorContributionId: string, associatedPreferencesModelUri: URI, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise | undefined> { return editor.setInput(input, options, context, token) .then(() => { if (token.isCancellationRequested) { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 870949d9c61..77b764417e7 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -39,7 +39,7 @@ import { attachButtonStyler, attachStylerCallback } from 'vs/platform/theme/comm import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { IUserDataAutoSyncService, IUserDataSyncService, SyncStatus } from 'vs/platform/userDataSync/common/userDataSync'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IEditorMemento, IEditorOpenContext, IEditorPane } from 'vs/workbench/common/editor'; import { attachSuggestEnabledInputBoxStyler, SuggestEnabledInput } from 'vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput'; import { SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; @@ -76,7 +76,7 @@ const searchBoxLabel = localize('SearchSettings.AriaLabel', "Search settings"); const SETTINGS_AUTOSAVE_NOTIFIED_KEY = 'hasNotifiedOfSettingsAutosave'; const SETTINGS_EDITOR_STATE_KEY = 'settingsEditorState'; -export class SettingsEditor2 extends BaseEditor { +export class SettingsEditor2 extends EditorPane { static readonly ID: string = 'workbench.editor.settings2'; private static NUM_INSTANCES: number = 0; @@ -202,7 +202,7 @@ export class SettingsEditor2 extends BaseEditor { get minimumWidth(): number { return 375; } get maximumWidth(): number { return Number.POSITIVE_INFINITY; } - // these setters need to exist because this extends from BaseEditor + // these setters need to exist because this extends from EditorPane set minimumWidth(value: number) { /*noop*/ } set maximumWidth(value: number) { /*noop*/ } diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts index a3c43f73982..5777d6d5c5b 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts @@ -11,7 +11,7 @@ import { isWeb } from 'vs/base/common/platform'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IEditorDropService } from 'vs/workbench/services/editor/browser/editorDropService'; import { EditorInput, EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; import { WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; @@ -21,7 +21,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; -export class WebviewEditor extends BaseEditor { +export class WebviewEditor extends EditorPane { public static readonly ID = 'WebviewEditor'; diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts index b8e307a3609..ac8ba77a324 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts @@ -11,7 +11,7 @@ import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { EditorOptions, IEditorMemento, IEditorOpenContext } from 'vs/workbench/common/editor'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput'; import { IOpenerService } from 'vs/platform/opener/common/opener'; @@ -55,7 +55,7 @@ interface IWalkThroughEditorViewState { viewState: IViewState; } -export class WalkThroughPart extends BaseEditor { +export class WalkThroughPart extends EditorPane { static readonly ID: string = 'workbench.editor.walkThroughPart'; 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 bec876bc4d3..817616f1ccd 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { EditorActivation } from 'vs/platform/editor/common/editor'; import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { EditorInput, EditorsOrder, SideBySideEditorInput } from 'vs/workbench/common/editor'; import { workbenchInstantiationService, TestServiceAccessor, registerTestEditor, TestFileEditorInput } from 'vs/workbench/test/browser/workbenchTestServices'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; @@ -386,7 +386,7 @@ suite('EditorService', () => { test('delegate', function (done) { const instantiationService = workbenchInstantiationService(); - class MyEditor extends BaseEditor { + class MyEditor extends EditorPane { constructor(id: string) { super(id, undefined!, new TestThemeService(), new TestStorageService()); diff --git a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts b/src/vs/workbench/test/browser/parts/editor/editorPane.test.ts similarity index 97% rename from src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts rename to src/vs/workbench/test/browser/parts/editor/editorPane.test.ts index 2255e8648e7..15b7aba94ce 100644 --- a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorPane.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { BaseEditor, EditorMemento } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorPane, EditorMemento } from 'vs/workbench/browser/parts/editor/editorPane'; import { EditorInput, EditorOptions, IEditorInputFactory, IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import * as Platform from 'vs/platform/registry/common/platform'; @@ -27,7 +27,7 @@ const NullThemeService = new TestThemeService(); let EditorRegistry: IEditorRegistry = Platform.Registry.as(Extensions.Editors); let EditorInputRegistry: IEditorInputFactoryRegistry = Platform.Registry.as(EditorExtensions.EditorInputFactories); -export class MyEditor extends BaseEditor { +export class MyEditor extends EditorPane { constructor(@ITelemetryService telemetryService: ITelemetryService) { super('MyEditor', NullTelemetryService, NullThemeService, new TestStorageService()); @@ -38,7 +38,7 @@ export class MyEditor extends BaseEditor { createEditor(): any { } } -export class MyOtherEditor extends BaseEditor { +export class MyOtherEditor extends EditorPane { constructor(@ITelemetryService telemetryService: ITelemetryService) { super('myOtherEditor', NullTelemetryService, NullThemeService, new TestStorageService()); @@ -96,9 +96,9 @@ class MyOtherInput extends EditorInput { } class MyResourceEditorInput extends ResourceEditorInput { } -suite('Workbench base editor', () => { +suite('Workbench EditorPane', () => { - test('BaseEditor API', async () => { + test('EditorPane API', async () => { let e = new MyEditor(NullTelemetryService); let input = new MyOtherInput(); let options = new EditorOptions(); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index c162c3e0320..0ee2a6325ba 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -91,7 +91,7 @@ import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { Registry } from 'vs/platform/registry/common/platform'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { CancellationToken } from 'vs/base/common/cancellation'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService'; @@ -1068,7 +1068,7 @@ export class TestEditorInput extends EditorInput { } export function registerTestEditor(id: string, inputs: SyncDescriptor[], factoryInputId?: string): IDisposable { - class TestEditorControl extends BaseEditor { + class TestEditor extends EditorPane { constructor() { super(id, NullTelemetryService, new TestThemeService(), new TestStorageService()); } @@ -1085,7 +1085,7 @@ export function registerTestEditor(id: string, inputs: SyncDescriptor(Extensions.Editors).registerEditor(EditorDescriptor.create(TestEditorControl, id, 'Test Editor Control'), inputs)); + disposables.add(Registry.as(Extensions.Editors).registerEditor(EditorDescriptor.create(TestEditor, id, 'Test Editor Control'), inputs)); if (factoryInputId) { From 984b654c8ba30a11239dcd2b394add3011455c40 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 24 Aug 2020 09:14:41 +0200 Subject: [PATCH 452/736] debt - remove unused function --- src/vs/workbench/api/common/extHostNotebook.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index ec334025bf7..60d466633d5 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -500,10 +500,6 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo getCell(cellHandle: number): ExtHostCell | undefined { return this._cells.find(cell => cell.handle === cellHandle); } - - getCell2(cellUri: UriComponents): ExtHostCell | undefined { - return this._cells.find(cell => cell.uri.fragment === cellUri.fragment); - } } export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellEdit { From cd18e7ec386f8f4ac2cf29d6da9a61f9d7e29a0a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 24 Aug 2020 10:11:45 +0200 Subject: [PATCH 453/736] debt - make ExtHostNotebookDocument not implement vscode.NotebookDocument but expose the API object via strict getter --- src/vs/vscode.proposed.d.ts | 1 + .../workbench/api/common/extHost.api.impl.ts | 2 +- .../workbench/api/common/extHostNotebook.ts | 299 +++++++++--------- .../test/browser/api/extHostNotebook.test.ts | 28 +- .../api/extHostNotebookConcatDocument.test.ts | 126 ++++---- 5 files changed, 223 insertions(+), 233 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 4c118533501..3e7a5110524 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1323,6 +1323,7 @@ declare module 'vscode' { export interface NotebookDocument { readonly uri: Uri; + readonly version: number; readonly fileName: string; readonly viewType: string; readonly isDirty: boolean; diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 748ddbe4c31..cbf9308517b 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -936,7 +936,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }, get notebookDocuments(): vscode.NotebookDocument[] { checkProposedApiEnabled(extension); - return extHostNotebook.notebookDocuments; + return extHostNotebook.notebookDocuments.map(d => d.notebookDocument); }, get visibleNotebookEditors() { checkProposedApiEnabled(extension); diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 60d466633d5..e013ab39e75 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -60,7 +60,7 @@ const addIdToOutput = (output: IRawOutput, id = UUID.generateUuid()): IProcessed export class ExtHostCell extends Disposable { - public static asModelAddData(notebook: ExtHostNotebookDocument, cell: IMainCellDto): IExtHostModelAddedData { + public static asModelAddData(notebook: vscode.NotebookDocument, cell: IMainCellDto): IExtHostModelAddedData { return { EOL: cell.eol, lines: cell.source, @@ -117,7 +117,7 @@ export class ExtHostCell extends Disposable { const that = this; const document = this._extHostDocument.getDocument(this.uri)!.document; this._cell = Object.freeze({ - notebook: that._notebook, + notebook: that._notebook.notebookDocument, uri: that.uri, cellKind: this._cellData.cellKind, document, @@ -175,7 +175,7 @@ export class ExtHostCell extends Disposable { } private _updateMetadata(): Promise { - return this._proxy.$updateNotebookCellMetadata(this._notebook.viewType, this._notebook.uri, this.handle, this._metadata); + return this._proxy.$updateNotebookCellMetadata(this._notebook.notebookDocument.viewType, this._notebook.uri, this.handle, this._metadata); } } @@ -193,7 +193,8 @@ class RawContentChangeEvent { } } -export class ExtHostNotebookDocument extends Disposable implements vscode.NotebookDocument { +export class ExtHostNotebookDocument extends Disposable { + private static _handlePool: number = 0; readonly handle = ExtHostNotebookDocument._handlePool++; @@ -201,33 +202,44 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo private _cellDisposableMapping = new Map(); - get cells(): ReadonlyArray { - return this._cells.map(cellData => cellData.cell); - } - - private _languages: string[] = []; - - get languages() { - return this._languages = []; - } - - set languages(newLanguages: string[]) { - this._languages = newLanguages; - this._proxy.$updateNotebookLanguages(this.viewType, this.uri, this._languages); - } - - get isUntitled() { - return this.uri.scheme === Schemas.untitled; - } + private _notebook: vscode.NotebookDocument | undefined; private _metadata: Required = notebookDocumentMetadataDefaults; private _metadataChangeListener: IDisposable; + private _displayOrder: string[] = []; + private _versionId = 0; + private _backupCounter = 1; + private _backup?: vscode.NotebookDocumentBackup; + private _disposed = false; + private _languages: string[] = []; - get metadata() { - return this._metadata; + private readonly _edits = new Cache('notebook documents'); + + constructor( + private readonly _proxy: MainThreadNotebookShape, + private readonly _documentsAndEditors: ExtHostDocumentsAndEditors, + private readonly _emitter: INotebookEventEmitter, + private readonly _viewType: string, + public readonly uri: URI, + public readonly renderingHandler: ExtHostNotebookOutputRenderingHandler, + private readonly _storagePath: URI | undefined + ) { + super(); + + const observableMetadata = getObservable(notebookDocumentMetadataDefaults); + this._metadata = observableMetadata.proxy; + this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => { + this._tryUpdateMetadata(); + })); } - set metadata(newMetadata: Required) { + dispose() { + this._disposed = true; + super.dispose(); + dispose(this._cellDisposableMapping.values()); + } + + private _updateMetadata(newMetadata: Required) { this._metadataChangeListener.dispose(); newMetadata = { ...notebookDocumentMetadataDefaults, @@ -240,91 +252,40 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo const observableMetadata = getObservable(newMetadata); this._metadata = observableMetadata.proxy; this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => { - this.updateMetadata(); + this._tryUpdateMetadata(); })); - this.updateMetadata(); + this._tryUpdateMetadata(); } - private _displayOrder: string[] = []; - - get displayOrder() { - return this._displayOrder; + private _tryUpdateMetadata() { + this._proxy.$updateNotebookMetadata(this._viewType, this.uri, this._metadata); } - - set displayOrder(newOrder: string[]) { - this._displayOrder = newOrder; - } - - private _versionId = 0; - - get versionId() { - return this._versionId; - } - - private _backupCounter = 1; - - private _backup?: vscode.NotebookDocumentBackup; - - - private readonly _edits = new Cache('notebook documents'); - - - addEdit(item: vscode.NotebookDocumentEditEvent): number { - return this._edits.add([item]); - } - - async undo(editId: number, isDirty: boolean): Promise { - await this.getEdit(editId).undo(); - // if (!isDirty) { - // this.disposeBackup(); - // } - } - - async redo(editId: number, isDirty: boolean): Promise { - await this.getEdit(editId).redo(); - // if (!isDirty) { - // this.disposeBackup(); - // } - } - - private getEdit(editId: number): vscode.NotebookDocumentEditEvent { - const edit = this._edits.get(editId, 0); - if (!edit) { - throw new Error('No edit found'); + get notebookDocument(): vscode.NotebookDocument { + if (!this._notebook) { + const that = this; + this._notebook = Object.freeze({ + get uri() { return that.uri; }, + get version() { return that._versionId; }, + get fileName() { return that.uri.fsPath; }, + get viewType() { return that._viewType; }, + get isDirty() { return false; }, + get isUntitled() { return that.uri.scheme === Schemas.untitled; }, + get cells(): ReadonlyArray { return that._cells.map(cell => cell.cell); }, + get languages() { return that._languages; }, + set languages(value: string[]) { that._trySetLanguages(value); }, + get displayOrder() { return that._displayOrder; }, + set displayOrder(value: string[]) { that._displayOrder = value; }, + get metadata() { return that._metadata; }, + set metadata(value: Required) { that._updateMetadata(value); }, + }); } - - return edit; + return this._notebook; } - disposeEdits(editIds: number[]): void { - for (const id of editIds) { - this._edits.delete(id); - } - } - - private _disposed = false; - - constructor( - private readonly _proxy: MainThreadNotebookShape, - private _documentsAndEditors: ExtHostDocumentsAndEditors, - private _emitter: INotebookEventEmitter, - public viewType: string, - public uri: URI, - public renderingHandler: ExtHostNotebookOutputRenderingHandler, - private readonly _storagePath: URI | undefined - ) { - super(); - - const observableMetadata = getObservable(notebookDocumentMetadataDefaults); - this._metadata = observableMetadata.proxy; - this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => { - this.updateMetadata(); - })); - } - - private updateMetadata() { - this._proxy.$updateNotebookMetadata(this.viewType, this.uri, this._metadata); + private _trySetLanguages(newLanguages: string[]) { + this._languages = newLanguages; + this._proxy.$updateNotebookLanguages(this._viewType, this.uri, this._languages); } getNewBackupUri(): URI { @@ -345,16 +306,6 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo this._backup = undefined; } - dispose() { - this._disposed = true; - super.dispose(); - dispose(this._cellDisposableMapping.values()); - } - - get fileName() { return this.uri.fsPath; } - - get isDirty() { return false; } - acceptModelChanged(event: NotebookCellsChangedEvent): void { this._versionId = event.versionId; if (event.kind === NotebookCellsChangeType.Initialize) { @@ -390,7 +341,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo const extCell = new ExtHostCell(this._proxy, this, this._documentsAndEditors, cell); if (!initialization) { - addedCellDocuments.push(ExtHostCell.asModelAddData(this, cell)); + addedCellDocuments.push(ExtHostCell.asModelAddData(this.notebookDocument, cell)); } if (!this._cellDisposableMapping.has(extCell.handle)) { @@ -426,7 +377,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo if (!initialization) { this._emitter.emitModelChange({ - document: this, + document: this.notebookDocument, changes: contentChangeEvents.map(RawContentChangeEvent.asApiEvent) }); } @@ -447,40 +398,40 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo items: cells.map(data => data.cell) }]; this._emitter.emitModelChange({ - document: this, + document: this.notebookDocument, changes }); } private _clearCellOutputs(index: number): void { - const cell = this.cells[index]; + const cell = this._cells[index].cell; cell.outputs = []; - const event: vscode.NotebookCellOutputsChangeEvent = { document: this, cells: [cell] }; + const event: vscode.NotebookCellOutputsChangeEvent = { document: this.notebookDocument, cells: [cell] }; this._emitter.emitCellOutputsChange(event); } private _clearAllCellOutputs(): void { const modifedCells: vscode.NotebookCell[] = []; - this.cells.forEach(cell => { + this._cells.forEach(({ cell }) => { if (cell.outputs.length !== 0) { cell.outputs = []; modifedCells.push(cell); } }); - const event: vscode.NotebookCellOutputsChangeEvent = { document: this, cells: modifedCells }; + const event: vscode.NotebookCellOutputsChangeEvent = { document: this.notebookDocument, cells: modifedCells }; this._emitter.emitCellOutputsChange(event); } private _changeCellLanguage(index: number, language: string): void { const cell = this._cells[index]; - const event: vscode.NotebookCellLanguageChangeEvent = { document: this, cell: cell.cell, language }; + const event: vscode.NotebookCellLanguageChangeEvent = { document: this.notebookDocument, cell: cell.cell, language }; this._emitter.emitCellLanguageChange(event); } private _changeCellMetadata(index: number, newMetadata: NotebookCellMetadata): void { const cell = this._cells[index]; cell.setMetadata(newMetadata); - const event: vscode.NotebookCellMetadataChangeEvent = { document: this, cell: cell.cell }; + const event: vscode.NotebookCellMetadataChangeEvent = { document: this.notebookDocument, cell: cell.cell }; this._emitter.emitCellMetadataChange(event); } @@ -490,9 +441,9 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo return [diff.start, diff.deleteCount, outputs]; }); - await this._proxy.$spliceNotebookCellOutputs(this.viewType, this.uri, cell.handle, outputDtos); + await this._proxy.$spliceNotebookCellOutputs(this._viewType, this.uri, cell.handle, outputDtos); this._emitter.emitCellOutputsChange({ - document: this, + document: this.notebookDocument, cells: [cell.cell] }); } @@ -500,6 +451,40 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo getCell(cellHandle: number): ExtHostCell | undefined { return this._cells.find(cell => cell.handle === cellHandle); } + + + addEdit(item: vscode.NotebookDocumentEditEvent): number { + return this._edits.add([item]); + } + + async undo(editId: number, isDirty: boolean): Promise { + await this.getEdit(editId).undo(); + // if (!isDirty) { + // this.disposeBackup(); + // } + } + + async redo(editId: number, isDirty: boolean): Promise { + await this.getEdit(editId).redo(); + // if (!isDirty) { + // this.disposeBackup(); + // } + } + + private getEdit(editId: number): vscode.NotebookDocumentEditEvent { + const edit = this._edits.get(editId, 0); + if (!edit) { + throw new Error('No edit found'); + } + + return edit; + } + + disposeEdits(editIds: number[]): void { + for (const id of editIds) { + this._edits.delete(id); + } + } } export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellEdit { @@ -511,7 +496,7 @@ export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellE constructor( readonly editor: ExtHostNotebookEditor ) { - this._documentVersionId = editor.document.versionId; + this._documentVersionId = editor.notebookData.notebookDocument.version; } finalize(): INotebookEditData { @@ -656,7 +641,7 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook public uri: URI, private _proxy: MainThreadNotebookShape, private _webComm: vscode.NotebookCommunication, - public document: ExtHostNotebookDocument, + public readonly notebookData: ExtHostNotebookDocument, ) { super(); this._register(this._webComm.onDidReceiveMessage(e => { @@ -664,6 +649,10 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook })); } + get document(): vscode.NotebookDocument { + return this.notebookData.notebookDocument; + } + edit(callback: (editBuilder: NotebookEditorCellEditBuilder) => void): Thenable { const edit = new NotebookEditorCellEditBuilder(this); callback(edit); @@ -759,7 +748,7 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable { } async provideKernels(document: ExtHostNotebookDocument, token: vscode.CancellationToken): Promise { - const data = await this._provider.provideKernels(document, token) || []; + const data = await this._provider.provideKernels(document.notebookDocument, token) || []; const newMap = new Map(); let kernel_unique_pool = 0; @@ -808,7 +797,7 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable { const kernel = this._idToKernel.get(kernelId); if (kernel && this._provider.resolveKernel) { - return this._provider.resolveKernel(kernel, document, webview, token); + return this._provider.resolveKernel(kernel, document.notebookDocument, webview, token); } } @@ -820,9 +809,9 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable { } if (cell) { - return withToken(token => (kernel.executeCell as any)(document, cell.cell, token)); + return withToken(token => (kernel.executeCell as any)(document.notebookDocument, cell.cell, token)); } else { - return withToken(token => (kernel.executeAllCells as any)(document, token)); + return withToken(token => (kernel.executeAllCells as any)(document.notebookDocument, token)); } } @@ -834,9 +823,9 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable { } if (cell) { - return kernel.cancelCellExecution(document, cell.cell); + return kernel.cancelCellExecution(document.notebookDocument, cell.cell); } else { - return kernel.cancelAllCellsExecution(document); + return kernel.cancelAllCellsExecution(document.notebookDocument); } } } @@ -896,7 +885,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN private _onDidSaveNotebookDocument = new Emitter(); onDidSaveNotebookDocument: Event = this._onDidCloseNotebookDocument.event; visibleNotebookEditors: ExtHostNotebookEditor[] = []; - private _onDidChangeActiveNotebookKernel = new Emitter<{ document: ExtHostNotebookDocument, kernel: vscode.NotebookKernel | undefined; }>(); + private _onDidChangeActiveNotebookKernel = new Emitter<{ document: vscode.NotebookDocument, kernel: vscode.NotebookKernel | undefined; }>(); onDidChangeActiveNotebookKernel = this._onDidChangeActiveNotebookKernel.event; private _onDidChangeVisibleNotebookEditors = new Emitter(); onDidChangeVisibleNotebookEditors = this._onDidChangeVisibleNotebookEditors.event; @@ -918,8 +907,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN const cellHandle = arg.cell.handle; for (const value of this._editors) { - if (value[1].editor.document.handle === documentHandle) { - const cell = value[1].editor.document.getCell(cellHandle); + if (value[1].editor.notebookData.handle === documentHandle) { + const cell = value[1].editor.notebookData.getCell(cellHandle); if (cell) { return cell; } @@ -1102,7 +1091,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN return; } - await provider.provider.resolveNotebook(document, webComm.contentProviderComm); + await provider.provider.resolveNotebook(document.notebookDocument, webComm.contentProviderComm); } async $executeNotebookByAttachedKernel(viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise { @@ -1139,9 +1128,9 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN if (provider.kernel) { if (cell) { - return provider.kernel.cancelCellExecution(document, cell.cell); + return provider.kernel.cancelCellExecution(document.notebookDocument, cell.cell); } else { - return provider.kernel.cancelAllCellsExecution(document); + return provider.kernel.cancelAllCellsExecution(document.notebookDocument); } } } @@ -1166,7 +1155,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN async $executeNotebook2(kernelId: string, viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise { const document = this._documents.get(URI.revive(uri)); - if (!document || document.viewType !== viewType) { + if (!document || document.notebookDocument.viewType !== viewType) { return; } @@ -1179,9 +1168,9 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined; if (cell) { - return withToken(token => (kernelInfo!.kernel.executeCell as any)(document, cell.cell, token)); + return withToken(token => (kernelInfo!.kernel.executeCell as any)(document.notebookDocument, cell.cell, token)); } else { - return withToken(token => (kernelInfo!.kernel.executeAllCells as any)(document, token)); + return withToken(token => (kernelInfo!.kernel.executeAllCells as any)(document.notebookDocument, token)); } } @@ -1192,7 +1181,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } if (this._notebookContentProviders.has(viewType)) { - await this._notebookContentProviders.get(viewType)!.provider.saveNotebook(document, token); + await this._notebookContentProviders.get(viewType)!.provider.saveNotebook(document.notebookDocument, token); return true; } @@ -1206,7 +1195,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } if (this._notebookContentProviders.has(viewType)) { - await this._notebookContentProviders.get(viewType)!.provider.saveNotebookAs(URI.revive(target), document, token); + await this._notebookContentProviders.get(viewType)!.provider.saveNotebookAs(URI.revive(target), document.notebookDocument, token); return true; } @@ -1238,7 +1227,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN const provider = this._notebookContentProviders.get(viewType); if (document && provider && provider.provider.backupNotebook) { - const backup = await provider.provider.backupNotebook(document, { destination: document.getNewBackupUri() }, cancellation); + const backup = await provider.provider.backupNotebook(document.notebookDocument, { destination: document.getNewBackupUri() }, cancellation); document.updateBackup(backup); return backup.id; } @@ -1255,11 +1244,11 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN this._withAdapter(event.providerHandle, event.uri, async (adapter, document) => { const kernel = event.kernelId ? adapter.getKernel(event.kernelId) : undefined; this._editors.forEach(editor => { - if (editor.editor.document === document) { + if (editor.editor.notebookData === document) { editor.editor.updateActiveKernel(kernel); } }); - this._onDidChangeActiveNotebookKernel.fire({ document, kernel }); + this._onDidChangeActiveNotebookKernel.fire({ document: document.notebookDocument, kernel }); }); } } @@ -1294,7 +1283,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN const document = this._documents.get(URI.revive(uriComponents)); if (document) { // this.$acceptDirtyStateChanged(uriComponents, false); - this._onDidSaveNotebookDocument.fire(document); + this._onDidSaveNotebookDocument.fire(document.notebookDocument); } } @@ -1309,14 +1298,14 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN if (data.selections) { if (data.selections.selections.length) { const firstCell = data.selections.selections[0]; - editor.editor.selection = editor.editor.document.getCell(firstCell)?.cell; + editor.editor.selection = editor.editor.notebookData.getCell(firstCell)?.cell; } else { editor.editor.selection = undefined; } } if (data.metadata) { - editor.editor.document.metadata = { + editor.editor.notebookData.notebookDocument.metadata = { ...notebookDocumentMetadataDefaults, ...data.metadata }; @@ -1333,7 +1322,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } const editor = new ExtHostNotebookEditor( - document.viewType, + document.notebookDocument.viewType, editorId, revivedUri, this._proxy, @@ -1343,7 +1332,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN if (selections.length) { const firstCell = selections[0]; - editor.selection = editor.document.getCell(firstCell)?.cell; + editor.selection = editor.notebookData.getCell(firstCell)?.cell; } else { editor.selection = undefined; } @@ -1363,8 +1352,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN if (document) { document.dispose(); this._documents.delete(revivedUri); - this._documentsAndEditors.$acceptDocumentsAndEditorsDelta({ removedDocuments: document.cells.map(cell => cell.uri) }); - this._onDidCloseNotebookDocument.fire(document); + this._documentsAndEditors.$acceptDocumentsAndEditorsDelta({ removedDocuments: document.notebookDocument.cells.map(cell => cell.uri) }); + this._onDidCloseNotebookDocument.fire(document.notebookDocument); } for (const e of this._editors.values()) { @@ -1408,7 +1397,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN this._unInitializedDocuments.delete(revivedUri); if (modelData.metadata) { - document.metadata = { + document.notebookDocument.metadata = { ...notebookDocumentMetadataDefaults, ...modelData.metadata }; @@ -1425,7 +1414,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN }); // add cell document as vscode.TextDocument - addedCellDocuments.push(...modelData.cells.map(cell => ExtHostCell.asModelAddData(document, cell))); + addedCellDocuments.push(...modelData.cells.map(cell => ExtHostCell.asModelAddData(document.notebookDocument, cell))); this._documents.get(revivedUri)?.dispose(); this._documents.set(revivedUri, document); @@ -1440,7 +1429,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN this._documentsAndEditors.$acceptDocumentsAndEditorsDelta({ addedDocuments: addedCellDocuments }); const document = this._documents.get(revivedUri)!; - this._onDidOpenNotebookDocument.fire(document); + this._onDidOpenNotebookDocument.fire(document.notebookDocument); } } diff --git a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts index cac9483fc13..42ea107dbc7 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts @@ -96,26 +96,26 @@ suite('NotebookCell#Document', function () { test('cell document is vscode.TextDocument', async function () { - assert.strictEqual(notebook.cells.length, 2); + assert.strictEqual(notebook.notebookDocument.cells.length, 2); - const [c1, c2] = notebook.cells; + const [c1, c2] = notebook.notebookDocument.cells; const d1 = extHostDocuments.getDocument(c1.uri); assert.ok(d1); assert.equal(d1.languageId, c1.language); assert.equal(d1.version, 1); - assert.ok(d1.notebook === notebook); + assert.ok(d1.notebook === notebook.notebookDocument); const d2 = extHostDocuments.getDocument(c2.uri); assert.ok(d2); assert.equal(d2.languageId, c2.language); assert.equal(d2.version, 1); - assert.ok(d2.notebook === notebook); + assert.ok(d2.notebook === notebook.notebookDocument); }); test('cell document goes when notebook closes', async function () { const cellUris: string[] = []; - for (let cell of notebook.cells) { + for (let cell of notebook.notebookDocument.cells) { assert.ok(extHostDocuments.getDocument(cell.uri)); cellUris.push(cell.uri.toString()); } @@ -160,7 +160,7 @@ suite('NotebookCell#Document', function () { extHostNotebooks.$acceptModelChanged(notebookUri, { kind: NotebookCellsChangeType.ModelChange, - versionId: notebook.versionId + 1, + versionId: notebook.notebookDocument.version + 1, changes: [[0, 0, [{ handle: 2, uri: CellUri.generate(notebookUri, 2), @@ -188,7 +188,7 @@ suite('NotebookCell#Document', function () { const docs: vscode.TextDocument[] = []; const addData: IModelAddedData[] = []; - for (let cell of notebook.cells) { + for (let cell of notebook.notebookDocument.cells) { const doc = extHostDocuments.getDocument(cell.uri); assert.ok(doc); assert.equal(extHostDocuments.getDocument(cell.uri).isClosed, false); @@ -210,14 +210,14 @@ suite('NotebookCell#Document', function () { extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ removedDocuments: docs.map(d => d.uri) }); // notebook is still open -> cell documents stay open - for (let cell of notebook.cells) { + for (let cell of notebook.notebookDocument.cells) { assert.ok(extHostDocuments.getDocument(cell.uri)); assert.equal(extHostDocuments.getDocument(cell.uri).isClosed, false); } // close notebook -> docs are closed extHostNotebooks.$acceptDocumentAndEditorsDelta({ removedDocuments: [notebook.uri] }); - for (let cell of notebook.cells) { + for (let cell of notebook.notebookDocument.cells) { assert.throws(() => extHostDocuments.getDocument(cell.uri)); } for (let doc of docs) { @@ -227,8 +227,8 @@ suite('NotebookCell#Document', function () { test('cell document goes when cell is removed', async function () { - assert.equal(notebook.cells.length, 2); - const [cell1, cell2] = notebook.cells; + assert.equal(notebook.notebookDocument.cells.length, 2); + const [cell1, cell2] = notebook.notebookDocument.cells; extHostNotebooks.$acceptModelChanged(notebook.uri, { kind: NotebookCellsChangeType.ModelChange, @@ -236,7 +236,7 @@ suite('NotebookCell#Document', function () { changes: [[0, 1, []]] }); - assert.equal(notebook.cells.length, 1); + assert.equal(notebook.notebookDocument.cells.length, 1); assert.equal(cell1.document.isClosed, true); // ref still alive! assert.equal(cell2.document.isClosed, false); @@ -244,8 +244,8 @@ suite('NotebookCell#Document', function () { }); test('cell document knows notebook', function () { - for (let cells of notebook.cells) { - assert.equal(cells.document.notebook === notebook, true); + for (let cells of notebook.notebookDocument.cells) { + assert.equal(cells.document.notebook === notebook.notebookDocument, true); } }); }); diff --git a/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts b/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts index d7326865a25..764eee4540c 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts @@ -88,7 +88,7 @@ suite('NotebookConcatDocument', function () { }); test('empty', function () { - let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined); + let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, undefined); assert.equal(doc.getText(), ''); assert.equal(doc.version, 0); @@ -125,7 +125,7 @@ suite('NotebookConcatDocument', function () { extHostNotebooks.$acceptModelChanged(notebookUri, { kind: NotebookCellsChangeType.ModelChange, - versionId: notebook.versionId + 1, + versionId: notebook.notebookDocument.version + 1, changes: [[0, 0, [{ handle: 1, uri: cellUri1, @@ -146,9 +146,9 @@ suite('NotebookConcatDocument', function () { }); - assert.equal(notebook.cells.length, 1 + 2); // markdown and code + assert.equal(notebook.notebookDocument.cells.length, 1 + 2); // markdown and code - let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined); + let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, undefined); assert.equal(doc.contains(cellUri1), true); assert.equal(doc.contains(cellUri2), true); @@ -159,7 +159,7 @@ suite('NotebookConcatDocument', function () { extHostNotebooks.$acceptModelChanged(notebookUri, { kind: NotebookCellsChangeType.ModelChange, - versionId: notebook.versionId + 1, + versionId: notebook.notebookDocument.version + 1, changes: [[0, 0, [{ handle: 1, uri: CellUri.generate(notebook.uri, 1), @@ -180,27 +180,27 @@ suite('NotebookConcatDocument', function () { }); - assert.equal(notebook.cells.length, 1 + 2); // markdown and code + assert.equal(notebook.notebookDocument.cells.length, 1 + 2); // markdown and code - let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined); + let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, undefined); assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!'); - assertLocation(doc, new Position(0, 0), new Location(notebook.cells[0].uri, new Position(0, 0))); - assertLocation(doc, new Position(4, 0), new Location(notebook.cells[1].uri, new Position(1, 0))); - assertLocation(doc, new Position(4, 3), new Location(notebook.cells[1].uri, new Position(1, 3))); - assertLocation(doc, new Position(5, 11), new Location(notebook.cells[1].uri, new Position(2, 11))); - assertLocation(doc, new Position(5, 12), new Location(notebook.cells[1].uri, new Position(2, 11)), false); // don't check identity because position will be clamped + assertLocation(doc, new Position(0, 0), new Location(notebook.notebookDocument.cells[0].uri, new Position(0, 0))); + assertLocation(doc, new Position(4, 0), new Location(notebook.notebookDocument.cells[1].uri, new Position(1, 0))); + assertLocation(doc, new Position(4, 3), new Location(notebook.notebookDocument.cells[1].uri, new Position(1, 3))); + assertLocation(doc, new Position(5, 11), new Location(notebook.notebookDocument.cells[1].uri, new Position(2, 11))); + assertLocation(doc, new Position(5, 12), new Location(notebook.notebookDocument.cells[1].uri, new Position(2, 11)), false); // don't check identity because position will be clamped }); test('location, position mapping, cell changes', function () { - let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined); + let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, undefined); // UPDATE 1 extHostNotebooks.$acceptModelChanged(notebookUri, { kind: NotebookCellsChangeType.ModelChange, - versionId: notebook.versionId + 1, + versionId: notebook.notebookDocument.version + 1, changes: [[0, 0, [{ handle: 1, uri: CellUri.generate(notebook.uri, 1), @@ -211,19 +211,19 @@ suite('NotebookConcatDocument', function () { outputs: [], }]]] }); - assert.equal(notebook.cells.length, 1 + 1); + assert.equal(notebook.notebookDocument.cells.length, 1 + 1); assert.equal(doc.version, 1); assertLines(doc, 'Hello', 'World', 'Hello World!'); - assertLocation(doc, new Position(0, 0), new Location(notebook.cells[0].uri, new Position(0, 0))); - assertLocation(doc, new Position(2, 2), new Location(notebook.cells[0].uri, new Position(2, 2))); - assertLocation(doc, new Position(4, 0), new Location(notebook.cells[0].uri, new Position(2, 12)), false); // clamped + assertLocation(doc, new Position(0, 0), new Location(notebook.notebookDocument.cells[0].uri, new Position(0, 0))); + assertLocation(doc, new Position(2, 2), new Location(notebook.notebookDocument.cells[0].uri, new Position(2, 2))); + assertLocation(doc, new Position(4, 0), new Location(notebook.notebookDocument.cells[0].uri, new Position(2, 12)), false); // clamped // UPDATE 2 extHostNotebooks.$acceptModelChanged(notebookUri, { kind: NotebookCellsChangeType.ModelChange, - versionId: notebook.versionId + 1, + versionId: notebook.notebookDocument.version + 1, changes: [[1, 0, [{ handle: 2, uri: CellUri.generate(notebook.uri, 2), @@ -235,37 +235,37 @@ suite('NotebookConcatDocument', function () { }]]] }); - assert.equal(notebook.cells.length, 1 + 2); + assert.equal(notebook.notebookDocument.cells.length, 1 + 2); assert.equal(doc.version, 2); assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!'); - assertLocation(doc, new Position(0, 0), new Location(notebook.cells[0].uri, new Position(0, 0))); - assertLocation(doc, new Position(4, 0), new Location(notebook.cells[1].uri, new Position(1, 0))); - assertLocation(doc, new Position(4, 3), new Location(notebook.cells[1].uri, new Position(1, 3))); - assertLocation(doc, new Position(5, 11), new Location(notebook.cells[1].uri, new Position(2, 11))); - assertLocation(doc, new Position(5, 12), new Location(notebook.cells[1].uri, new Position(2, 11)), false); // don't check identity because position will be clamped + assertLocation(doc, new Position(0, 0), new Location(notebook.notebookDocument.cells[0].uri, new Position(0, 0))); + assertLocation(doc, new Position(4, 0), new Location(notebook.notebookDocument.cells[1].uri, new Position(1, 0))); + assertLocation(doc, new Position(4, 3), new Location(notebook.notebookDocument.cells[1].uri, new Position(1, 3))); + assertLocation(doc, new Position(5, 11), new Location(notebook.notebookDocument.cells[1].uri, new Position(2, 11))); + assertLocation(doc, new Position(5, 12), new Location(notebook.notebookDocument.cells[1].uri, new Position(2, 11)), false); // don't check identity because position will be clamped // UPDATE 3 (remove cell #2 again) extHostNotebooks.$acceptModelChanged(notebookUri, { kind: NotebookCellsChangeType.ModelChange, - versionId: notebook.versionId + 1, + versionId: notebook.notebookDocument.version + 1, changes: [[1, 1, []]] }); - assert.equal(notebook.cells.length, 1 + 1); + assert.equal(notebook.notebookDocument.cells.length, 1 + 1); assert.equal(doc.version, 3); assertLines(doc, 'Hello', 'World', 'Hello World!'); - assertLocation(doc, new Position(0, 0), new Location(notebook.cells[0].uri, new Position(0, 0))); - assertLocation(doc, new Position(2, 2), new Location(notebook.cells[0].uri, new Position(2, 2))); - assertLocation(doc, new Position(4, 0), new Location(notebook.cells[0].uri, new Position(2, 12)), false); // clamped + assertLocation(doc, new Position(0, 0), new Location(notebook.notebookDocument.cells[0].uri, new Position(0, 0))); + assertLocation(doc, new Position(2, 2), new Location(notebook.notebookDocument.cells[0].uri, new Position(2, 2))); + assertLocation(doc, new Position(4, 0), new Location(notebook.notebookDocument.cells[0].uri, new Position(2, 12)), false); // clamped }); test('location, position mapping, cell-document changes', function () { - let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined); + let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, undefined); // UPDATE 1 extHostNotebooks.$acceptModelChanged(notebookUri, { kind: NotebookCellsChangeType.ModelChange, - versionId: notebook.versionId + 1, + versionId: notebook.notebookDocument.version + 1, changes: [[0, 0, [{ handle: 1, uri: CellUri.generate(notebook.uri, 1), @@ -284,21 +284,21 @@ suite('NotebookConcatDocument', function () { outputs: [], }]]] }); - assert.equal(notebook.cells.length, 1 + 2); + assert.equal(notebook.notebookDocument.cells.length, 1 + 2); assert.equal(doc.version, 1); assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!'); - assertLocation(doc, new Position(0, 0), new Location(notebook.cells[0].uri, new Position(0, 0))); - assertLocation(doc, new Position(2, 2), new Location(notebook.cells[0].uri, new Position(2, 2))); - assertLocation(doc, new Position(2, 12), new Location(notebook.cells[0].uri, new Position(2, 12))); - assertLocation(doc, new Position(4, 0), new Location(notebook.cells[1].uri, new Position(1, 0))); - assertLocation(doc, new Position(4, 3), new Location(notebook.cells[1].uri, new Position(1, 3))); + assertLocation(doc, new Position(0, 0), new Location(notebook.notebookDocument.cells[0].uri, new Position(0, 0))); + assertLocation(doc, new Position(2, 2), new Location(notebook.notebookDocument.cells[0].uri, new Position(2, 2))); + assertLocation(doc, new Position(2, 12), new Location(notebook.notebookDocument.cells[0].uri, new Position(2, 12))); + assertLocation(doc, new Position(4, 0), new Location(notebook.notebookDocument.cells[1].uri, new Position(1, 0))); + assertLocation(doc, new Position(4, 3), new Location(notebook.notebookDocument.cells[1].uri, new Position(1, 3))); // offset math let cell1End = doc.offsetAt(new Position(2, 12)); assert.equal(doc.positionAt(cell1End).isEqual(new Position(2, 12)), true); - extHostDocuments.$acceptModelChanged(notebook.cells[0].uri, { + extHostDocuments.$acceptModelChanged(notebook.notebookDocument.cells[0].uri, { versionId: 0, eol: '\n', changes: [{ @@ -309,7 +309,7 @@ suite('NotebookConcatDocument', function () { }] }, false); assertLines(doc, 'Hello', 'World', 'Hi World!', 'Hallo', 'Welt', 'Hallo Welt!'); - assertLocation(doc, new Position(2, 12), new Location(notebook.cells[0].uri, new Position(2, 9)), false); + assertLocation(doc, new Position(2, 12), new Location(notebook.notebookDocument.cells[0].uri, new Position(2, 9)), false); assert.equal(doc.positionAt(cell1End).isEqual(new Position(3, 2)), true); @@ -319,7 +319,7 @@ suite('NotebookConcatDocument', function () { extHostNotebooks.$acceptModelChanged(notebookUri, { kind: NotebookCellsChangeType.ModelChange, - versionId: notebook.versionId + 1, + versionId: notebook.notebookDocument.version + 1, changes: [[0, 0, [{ handle: 1, uri: CellUri.generate(notebook.uri, 1), @@ -339,9 +339,9 @@ suite('NotebookConcatDocument', function () { }]]] }); - const mixedDoc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined); - const fooLangDoc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, 'fooLang'); - const barLangDoc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, 'barLang'); + const mixedDoc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, undefined); + const fooLangDoc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, 'fooLang'); + const barLangDoc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, 'barLang'); assertLines(mixedDoc, 'fooLang-document', 'barLang-document'); assertLines(fooLangDoc, 'fooLang-document'); @@ -349,7 +349,7 @@ suite('NotebookConcatDocument', function () { extHostNotebooks.$acceptModelChanged(notebookUri, { kind: NotebookCellsChangeType.ModelChange, - versionId: notebook.versionId + 1, + versionId: notebook.notebookDocument.version + 1, changes: [[2, 0, [{ handle: 3, uri: CellUri.generate(notebook.uri, 3), @@ -383,7 +383,7 @@ suite('NotebookConcatDocument', function () { extHostNotebooks.$acceptModelChanged(notebookUri, { kind: NotebookCellsChangeType.ModelChange, - versionId: notebook.versionId + 1, + versionId: notebook.notebookDocument.version + 1, changes: [[0, 0, [{ handle: 1, uri: CellUri.generate(notebook.uri, 1), @@ -403,9 +403,9 @@ suite('NotebookConcatDocument', function () { }]]] }); - assert.equal(notebook.cells.length, 1 + 2); // markdown and code + assert.equal(notebook.notebookDocument.cells.length, 1 + 2); // markdown and code - let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined); + let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, undefined); assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!'); assertOffsetAtPosition(doc, 0, { line: 0, character: 0 }); @@ -436,7 +436,7 @@ suite('NotebookConcatDocument', function () { extHostNotebooks.$acceptModelChanged(notebookUri, { kind: NotebookCellsChangeType.ModelChange, - versionId: notebook.versionId + 1, + versionId: notebook.notebookDocument.version + 1, changes: [[0, 0, [{ handle: 1, uri: CellUri.generate(notebook.uri, 1), @@ -456,24 +456,24 @@ suite('NotebookConcatDocument', function () { }]]] }); - assert.equal(notebook.cells.length, 1 + 2); // markdown and code + assert.equal(notebook.notebookDocument.cells.length, 1 + 2); // markdown and code - let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined); + let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, undefined); assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!'); - assertLocationAtPosition(doc, { line: 0, character: 0 }, { uri: notebook.cells[0].uri, line: 0, character: 0 }); - assertLocationAtPosition(doc, { line: 2, character: 0 }, { uri: notebook.cells[0].uri, line: 2, character: 0 }); - assertLocationAtPosition(doc, { line: 2, character: 12 }, { uri: notebook.cells[0].uri, line: 2, character: 12 }); - assertLocationAtPosition(doc, { line: 3, character: 0 }, { uri: notebook.cells[1].uri, line: 0, character: 0 }); - assertLocationAtPosition(doc, { line: 5, character: 0 }, { uri: notebook.cells[1].uri, line: 2, character: 0 }); - assertLocationAtPosition(doc, { line: 5, character: 11 }, { uri: notebook.cells[1].uri, line: 2, character: 11 }); + assertLocationAtPosition(doc, { line: 0, character: 0 }, { uri: notebook.notebookDocument.cells[0].uri, line: 0, character: 0 }); + assertLocationAtPosition(doc, { line: 2, character: 0 }, { uri: notebook.notebookDocument.cells[0].uri, line: 2, character: 0 }); + assertLocationAtPosition(doc, { line: 2, character: 12 }, { uri: notebook.notebookDocument.cells[0].uri, line: 2, character: 12 }); + assertLocationAtPosition(doc, { line: 3, character: 0 }, { uri: notebook.notebookDocument.cells[1].uri, line: 0, character: 0 }); + assertLocationAtPosition(doc, { line: 5, character: 0 }, { uri: notebook.notebookDocument.cells[1].uri, line: 2, character: 0 }); + assertLocationAtPosition(doc, { line: 5, character: 11 }, { uri: notebook.notebookDocument.cells[1].uri, line: 2, character: 11 }); }); test('getText(range)', function () { extHostNotebooks.$acceptModelChanged(notebookUri, { kind: NotebookCellsChangeType.ModelChange, - versionId: notebook.versionId + 1, + versionId: notebook.notebookDocument.version + 1, changes: [[0, 0, [{ handle: 1, uri: CellUri.generate(notebook.uri, 1), @@ -493,9 +493,9 @@ suite('NotebookConcatDocument', function () { }]]] }); - assert.equal(notebook.cells.length, 1 + 2); // markdown and code + assert.equal(notebook.notebookDocument.cells.length, 1 + 2); // markdown and code - let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined); + let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, undefined); assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!'); assert.equal(doc.getText(new Range(0, 0, 0, 0)), ''); @@ -507,7 +507,7 @@ suite('NotebookConcatDocument', function () { extHostNotebooks.$acceptModelChanged(notebookUri, { kind: NotebookCellsChangeType.ModelChange, - versionId: notebook.versionId + 1, + versionId: notebook.notebookDocument.version + 1, changes: [[0, 0, [{ handle: 1, uri: CellUri.generate(notebook.uri, 1), @@ -527,9 +527,9 @@ suite('NotebookConcatDocument', function () { }]]] }); - assert.equal(notebook.cells.length, 1 + 2); // markdown and code + assert.equal(notebook.notebookDocument.cells.length, 1 + 2); // markdown and code - let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined); + let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, undefined); assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!'); From 890d7c61af3f6664b2c1460ad5af4274455a0b04 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Mon, 24 Aug 2020 10:54:51 +0200 Subject: [PATCH 454/736] node-debug@1.44.11 --- product.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product.json b/product.json index 70c95edd0d4..b72e7e53dee 100644 --- a/product.json +++ b/product.json @@ -31,7 +31,7 @@ "builtInExtensions": [ { "name": "ms-vscode.node-debug", - "version": "1.44.10", + "version": "1.44.11", "repo": "https://github.com/Microsoft/vscode-node-debug", "metadata": { "id": "b6ded8fb-a0a0-4c1c-acbd-ab2a3bc995a6", From 653e7e26df3be4cc5185982a8a4780b44523ef8e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 24 Aug 2020 11:09:22 +0200 Subject: [PATCH 455/736] sandbox - make lazyEnv fit for sandbox use --- .../parts/sandbox/electron-browser/preload.js | 36 +++++++++++++++ .../parts/sandbox/electron-sandbox/globals.ts | 6 +++ .../electron-browser/workbench/workbench.js | 44 +++++-------------- src/vs/code/electron-main/app.ts | 9 ++-- src/vs/code/node/shellEnv.ts | 19 ++++---- .../node/externalTerminalService.ts | 13 ------ 6 files changed, 69 insertions(+), 58 deletions(-) diff --git a/src/vs/base/parts/sandbox/electron-browser/preload.js b/src/vs/base/parts/sandbox/electron-browser/preload.js index 4dbe1b48a1d..d6bea974701 100644 --- a/src/vs/base/parts/sandbox/electron-browser/preload.js +++ b/src/vs/base/parts/sandbox/electron-browser/preload.js @@ -93,6 +93,14 @@ process: { platform: process.platform, env: process.env, + _whenEnvResolved: undefined, + get whenEnvResolved() { + if (!this._whenEnvResolved) { + this._whenEnvResolved = resolveEnv(); + } + + return this._whenEnvResolved; + }, on: /** * @param {string} type @@ -157,5 +165,33 @@ return true; } + /** + * If VSCode is not run from a terminal, we should resolve additional + * shell specific environment from the OS shell to ensure we are seeing + * all development related environment variables. We do this from the + * main process because it may involve spawning a shell. + */ + function resolveEnv() { + return new Promise(function (resolve) { + const handle = setTimeout(function () { + console.warn('Preload: Unable to resolve shell environment in a reasonable time'); + + // It took too long to fetch the shell environment, return + resolve(); + }, 3000); + + ipcRenderer.once('vscode:acceptShellEnv', function (event, shellEnv) { + clearTimeout(handle); + + // Assign all keys of the shell environment to our process environment + Object.assign(process.env, shellEnv); + + resolve(); + }); + + ipcRenderer.send('vscode:fetchShellEnv'); + }); + } + //#endregion }()); diff --git a/src/vs/base/parts/sandbox/electron-sandbox/globals.ts b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts index 0acd55cb3fe..a305df1c8a3 100644 --- a/src/vs/base/parts/sandbox/electron-sandbox/globals.ts +++ b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts @@ -84,6 +84,12 @@ export const process = (window as any).vscode.process as { */ env: { [key: string]: string | undefined }; + /** + * Allows to await resolving the full process environment by checking for the shell environment + * of the OS in certain cases (e.g. when the app is started from the Dock on macOS). + */ + whenEnvResolved: Promise; + /** * A listener on the process. Only a small subset of listener types are allowed. */ diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index d5b7ff6728e..6642ac864eb 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -33,8 +33,8 @@ const bootstrapWindow = (() => { return window.MonacoBootstrapWindow; })(); -// Setup shell environment -process['lazyEnv'] = getLazyEnv(); +// Load environment in parallel to workbench loading to avoid waterfall +const whenEnvResolved = bootstrapWindow.globals().process.whenEnvResolved; // Load workbench main JS, CSS and NLS all in parallel. This is an // optimization to prevent a waterfall of loading to happen, because @@ -45,23 +45,26 @@ bootstrapWindow.load([ 'vs/nls!vs/workbench/workbench.desktop.main', 'vs/css!vs/workbench/workbench.desktop.main' ], - function (workbench, configuration) { + async function (workbench, configuration) { // Mark start of workbench perf.mark('didLoadWorkbenchMain'); performance.mark('workbench-start'); - return process['lazyEnv'].then(function () { - perf.mark('main/startup'); + // Wait for process environment being fully resolved + await whenEnvResolved; - // @ts-ignore - return require('vs/workbench/electron-browser/desktop.main').main(configuration); - }); + perf.mark('main/startup'); + + // @ts-ignore + return require('vs/workbench/electron-browser/desktop.main').main(configuration); }, { removeDeveloperKeybindingsAfterLoad: true, canModifyDOM: function (windowConfig) { - showPartsSplash(windowConfig); + if (!bootstrapWindow.globals().context.sandbox) { + showPartsSplash(windowConfig); // TODO@sandbox non-sandboxed only + } }, beforeLoaderConfig: function (windowConfig, loaderConfig) { loaderConfig.recordStats = true; @@ -171,26 +174,3 @@ function showPartsSplash(configuration) { perf.mark('didShowPartsSplash'); } - -/** - * @returns {Promise} - */ -function getLazyEnv() { - const ipcRenderer = bootstrapWindow.globals().ipcRenderer; - - return new Promise(function (resolve) { - const handle = setTimeout(function () { - resolve(); - console.warn('renderer did not receive lazyEnv in time'); - }, 10000); - - ipcRenderer.once('vscode:acceptShellEnv', function (event, shellEnv) { - clearTimeout(handle); - Object.assign(process.env, shellEnv); - // @ts-ignore - resolve(process.env); - }); - - ipcRenderer.send('vscode:fetchShellEnv'); - }); -} diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index e8ff8dda77a..21a4eeb08e0 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -364,17 +364,17 @@ export class CodeApplication extends Disposable { const sharedProcess = this.instantiationService.createInstance(SharedProcess, machineId, this.userEnv); const sharedProcessClient = sharedProcess.whenIpcReady().then(() => { this.logService.trace('Shared process: IPC ready'); + return connect(this.environmentService.sharedIPCHandle, 'main'); }); const sharedProcessReady = sharedProcess.whenReady().then(() => { this.logService.trace('Shared process: init ready'); + return sharedProcessClient; }); this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => { this._register(new RunOnceScheduler(async () => { - const userEnv = await getShellEnvironment(this.logService, this.environmentService); - - sharedProcess.spawn(userEnv); + sharedProcess.spawn(await getShellEnvironment(this.logService, this.environmentService)); }, 3000)).schedule(); }); @@ -847,6 +847,9 @@ export class CodeApplication extends Disposable { } catch (error) { this.logService.error(error); } + + // Start to fetch shell environment after window has opened + getShellEnvironment(this.logService, this.environmentService); } private handleRemoteAuthorities(): void { diff --git a/src/vs/code/node/shellEnv.ts b/src/vs/code/node/shellEnv.ts index 174bb673a4a..0383550627a 100644 --- a/src/vs/code/node/shellEnv.ts +++ b/src/vs/code/node/shellEnv.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as cp from 'child_process'; +import { spawn } from 'child_process'; import { generateUuid } from 'vs/base/common/uuid'; import { isWindows } from 'vs/base/common/platform'; import { ILogService } from 'vs/platform/log/common/log'; @@ -30,7 +30,7 @@ function getUnixShellEnvironment(logService: ILogService): Promise ({})); } - -let _shellEnv: Promise; +let shellEnvPromise: Promise | undefined = undefined; /** * We need to get the environment from a user's shell. @@ -91,21 +90,21 @@ let _shellEnv: Promise; * from within a shell. */ export function getShellEnvironment(logService: ILogService, environmentService: INativeEnvironmentService): Promise { - if (_shellEnv === undefined) { + if (!shellEnvPromise) { if (environmentService.args['disable-user-env-probe']) { logService.trace('getShellEnvironment: disable-user-env-probe set, skipping'); - _shellEnv = Promise.resolve({}); + shellEnvPromise = Promise.resolve({}); } else if (isWindows) { logService.trace('getShellEnvironment: running on Windows, skipping'); - _shellEnv = Promise.resolve({}); + shellEnvPromise = Promise.resolve({}); } else if (process.env['VSCODE_CLI'] === '1' && process.env['VSCODE_FORCE_USER_ENV'] !== '1') { logService.trace('getShellEnvironment: running on CLI, skipping'); - _shellEnv = Promise.resolve({}); + shellEnvPromise = Promise.resolve({}); } else { logService.trace('getShellEnvironment: running on Unix'); - _shellEnv = getUnixShellEnvironment(logService); + shellEnvPromise = getUnixShellEnvironment(logService); } } - return _shellEnv; + return shellEnvPromise; } diff --git a/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.ts b/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.ts index 99ac1f65419..42dba3506e5 100644 --- a/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.ts +++ b/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.ts @@ -18,18 +18,6 @@ import { DEFAULT_TERMINAL_OSX } from 'vs/workbench/contrib/externalTerminal/node const TERMINAL_TITLE = nls.localize('console.title', "VS Code Console"); -type LazyProcess = { - - /** - * The lazy environment is a promise that resolves to `process.env` - * once the process is resolved. The use-case is VS Code running - * on Linux/macOS when being launched via a launcher. Then the env - * (as defined in .bashrc etc) isn't properly set and needs to be - * resolved lazy. - */ - lazyEnv: Promise | undefined; -}; - export class WindowsExternalTerminalService implements IExternalTerminalService { public _serviceBrand: undefined; @@ -318,7 +306,6 @@ export class LinuxExternalTerminalService implements IExternalTerminalService { LinuxExternalTerminalService._DEFAULT_TERMINAL_LINUX_READY = new Promise(async r => { if (env.isLinux) { const isDebian = await pfs.exists('/etc/debian_version'); - await (process as unknown as LazyProcess).lazyEnv; if (isDebian) { r('x-terminal-emulator'); } else if (process.env.DESKTOP_SESSION === 'gnome' || process.env.DESKTOP_SESSION === 'gnome-classic') { From f7a687ed42dd6b984d4090261057a5754f1f8033 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 24 Aug 2020 11:14:16 +0200 Subject: [PATCH 456/736] On Type Rename tests consume 20% of all unit test time. Fixes #105269 --- src/vs/editor/contrib/rename/onTypeRename.ts | 13 ++++++++++--- .../editor/contrib/rename/test/onTypeRename.test.ts | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/rename/onTypeRename.ts b/src/vs/editor/contrib/rename/onTypeRename.ts index 70cef23cc3a..105dfb2ddb2 100644 --- a/src/vs/editor/contrib/rename/onTypeRename.ts +++ b/src/vs/editor/contrib/rename/onTypeRename.ts @@ -46,6 +46,8 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr return editor.getContribution(OnTypeRenameContribution.ID); } + private _debounceDuration = 200; + private readonly _editor: ICodeEditor; private _enabled: boolean; @@ -121,15 +123,15 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr this._languageWordPattern = LanguageConfigurationRegistry.getWordDefinition(model.getLanguageIdentifier().id); })); - const rangeUpdateScheduler = new Delayer(200); + const rangeUpdateScheduler = new Delayer(this._debounceDuration); const triggerRangeUpdate = () => { - this._rangeUpdateTriggerPromise = rangeUpdateScheduler.trigger(() => this.updateRanges()); + this._rangeUpdateTriggerPromise = rangeUpdateScheduler.trigger(() => this.updateRanges(), this._debounceDuration); }; const rangeSyncScheduler = new Delayer(0); const triggerRangeSync = (decorations: string[]) => { this._rangeSyncTriggerPromise = rangeSyncScheduler.trigger(() => this._syncRanges(decorations)); }; - this._localToDispose.add(this._editor.onDidChangeCursorPosition((e) => { + this._localToDispose.add(this._editor.onDidChangeCursorPosition(() => { triggerRangeUpdate(); })); this._localToDispose.add(this._editor.onDidChangeModelContent((e) => { @@ -332,6 +334,11 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr return request; } + // for testing + public setDebounceDuration(timeInMS: number) { + this._debounceDuration = timeInMS; + } + // private printDecorators(model: ITextModel) { // return this._currentDecorations.map(d => { // const range = model.getDecorationRange(d); diff --git a/src/vs/editor/contrib/rename/test/onTypeRename.test.ts b/src/vs/editor/contrib/rename/test/onTypeRename.test.ts index 0b5fd0b6b56..303b3554445 100644 --- a/src/vs/editor/contrib/rename/test/onTypeRename.test.ts +++ b/src/vs/editor/contrib/rename/test/onTypeRename.test.ts @@ -79,6 +79,7 @@ suite('On type rename', () => { OnTypeRenameContribution.ID, OnTypeRenameContribution ); + ontypeRenameContribution.setDebounceDuration(0); const testEditor: TestEditor = { setPosition(pos: Position) { From 834a380d93cab18b44f52fcbb3db4ddd752ddf60 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 24 Aug 2020 11:01:08 +0200 Subject: [PATCH 457/736] add `replaceCells` to notebook edit builder, also refactor cell source to only be a string, not an array --- src/vs/vscode.proposed.d.ts | 11 +- .../api/browser/mainThreadNotebook.ts | 2 +- .../workbench/api/common/extHost.protocol.ts | 2 +- .../workbench/api/common/extHostNotebook.ts | 81 ++++--- .../contrib/fold/test/notebookFolding.test.ts | 204 +++++++++--------- .../notebook/browser/notebookEditorWidget.ts | 2 +- .../browser/viewModel/notebookViewModel.ts | 4 +- .../common/model/notebookCellTextModel.ts | 4 +- .../common/model/notebookTextModel.ts | 4 +- .../contrib/notebook/common/notebookCommon.ts | 3 +- .../notebook/test/notebookCommon.test.ts | 6 +- .../notebook/test/notebookTextModel.test.ts | 53 +++-- .../notebook/test/notebookViewModel.test.ts | 68 +++--- .../notebook/test/testNotebookEditor.ts | 4 +- 14 files changed, 231 insertions(+), 217 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 3e7a5110524..3a1ad120a9d 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1354,7 +1354,12 @@ declare module 'vscode' { } export interface NotebookEditorCellEdit { + + replaceCells(from: number, to: number, cells: NotebookCellData[]): void; + + /** @deprecated */ insert(index: number, content: string | string[], language: string, type: CellKind, outputs: CellOutput[], metadata: NotebookCellMetadata | undefined): void; + /** @deprecated */ delete(index: number): void; } @@ -1478,9 +1483,9 @@ declare module 'vscode' { export interface NotebookCellData { readonly cellKind: CellKind; readonly source: string; - language: string; - outputs: CellOutput[]; - metadata: NotebookCellMetadata; + readonly language: string; + readonly outputs: CellOutput[]; + readonly metadata: NotebookCellMetadata | undefined; } export interface NotebookData { diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 1fdf3135d25..c767266373d 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -452,7 +452,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo if (data.cells.length) { textModel.initialize(data!.cells); } else { - const mainCell = textModel.createCellTextModel([''], textModel.languages.length ? textModel.languages[0] : '', CellKind.Code, [], undefined); + const mainCell = textModel.createCellTextModel('', textModel.languages.length ? textModel.languages[0] : '', CellKind.Code, [], undefined); textModel.insertTemplateCell(mainCell); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 99af61c1e7e..e09f81490e8 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -723,7 +723,7 @@ export interface MainThreadNotebookShape extends IDisposable { $unregisterNotebookKernelProvider(handle: number): Promise; $onNotebookKernelChange(handle: number): void; $unregisterNotebookKernel(id: string): Promise; - $tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[], renderers: number[]): Promise; + $tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[]): Promise; $updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise; $updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata): Promise; $updateNotebookCellMetadata(viewType: string, resource: UriComponents, handle: number, metadata: NotebookCellMetadata | undefined): Promise; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index e013ab39e75..22c52d2e7ed 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -22,7 +22,7 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; -import { CellEditType, CellOutputKind, diff, ICellDeleteEdit, ICellEditOperation, ICellInsertEdit, IMainCellDto, INotebookDisplayOrder, INotebookEditData, INotebookKernelInfoDto2, IProcessedOutput, IRawOutput, NotebookCellMetadata, NotebookCellsChangedEvent, NotebookCellsChangeType, NotebookCellsSplice2, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, CellOutputKind, diff, ICellDeleteEdit, ICellDto2, ICellEditOperation, ICellInsertEdit, IMainCellDto, INotebookDisplayOrder, INotebookEditData, INotebookKernelInfoDto2, IProcessedOutput, IRawOutput, NotebookCellMetadata, NotebookCellsChangedEvent, NotebookCellsChangeType, NotebookCellsSplice2, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import * as vscode from 'vscode'; import { Cache } from './cache'; import { ResourceMap } from 'vs/base/common/map'; @@ -488,15 +488,13 @@ export class ExtHostNotebookDocument extends Disposable { } export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellEdit { - private _finalized: boolean = false; - private readonly _documentVersionId: number; - private _collectedEdits: ICellEditOperation[] = []; - private _renderers = new Set(); - constructor( - readonly editor: ExtHostNotebookEditor - ) { - this._documentVersionId = editor.notebookData.notebookDocument.version; + private readonly _documentVersionId: number; + private readonly _collectedEdits: ICellEditOperation[] = []; + private _finalized: boolean = false; + + constructor(documentVersionId: number) { + this._documentVersionId = documentVersionId; } finalize(): INotebookEditData { @@ -504,7 +502,6 @@ export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellE return { documentVersionId: this._documentVersionId, edits: this._collectedEdits, - renderers: Array.from(this._renderers) }; } @@ -514,33 +511,48 @@ export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellE } } - insert(index: number, content: string | string[], language: string, type: CellKind, outputs: vscode.CellOutput[], metadata: vscode.NotebookCellMetadata | undefined): void { + replaceCells(from: number, to: number, cells: vscode.NotebookCellData[]): void { this._throwIfFinalized(); - const sourceArr = Array.isArray(content) ? content : content.split(/\r|\n|\r\n/g); - const cell = { - source: sourceArr, - language, - cellKind: type, - outputs: outputs.map(o => addIdToOutput(o)), - metadata, - }; + // deletion + if (to > from) { + this._collectedEdits.push({ + editType: CellEditType.Delete, + index: from, + count: to - from + }); + } + // insert + if (cells.length > 0) { + this._collectedEdits.push({ + editType: CellEditType.Insert, + index: from, + cells: cells.map(data => { + return { + cellKind: data.cellKind, + language: data.language, + outputs: data.outputs.map(output => addIdToOutput(output)), + source: data.source + }; + }) + }); + } + } - this._collectedEdits.push({ - editType: CellEditType.Insert, - index, - cells: [cell] - }); + insert(index: number, content: string | string[], language: string, type: CellKind, outputs: vscode.CellOutput[], metadata: vscode.NotebookCellMetadata | undefined): void { + this._throwIfFinalized(); + this.replaceCells(index, index, [{ + language, + outputs, + metadata, + cellKind: type, + source: Array.isArray(content) ? content.join('\n') : content, + }]); } delete(index: number): void { this._throwIfFinalized(); - - this._collectedEdits.push({ - editType: CellEditType.Delete, - index, - count: 1 - }); + this.replaceCells(index, 1, []); } } @@ -654,13 +666,12 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook } edit(callback: (editBuilder: NotebookEditorCellEditBuilder) => void): Thenable { - const edit = new NotebookEditorCellEditBuilder(this); + const edit = new NotebookEditorCellEditBuilder(this.document.version); callback(edit); - return this._applyEdit(edit); + return this._applyEdit(edit.finalize()); } - private _applyEdit(editBuilder: NotebookEditorCellEditBuilder): Promise { - const editData = editBuilder.finalize(); + private _applyEdit(editData: INotebookEditData): Promise { // return when there is nothing to do if (editData.edits.length === 0) { @@ -698,7 +709,7 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook compressedEditsIndex++; } - return this._proxy.$tryApplyEdits(this.viewType, this.uri, editData.documentVersionId, compressedEdits, editData.renderers); + return this._proxy.$tryApplyEdits(this.viewType, this.uri, editData.documentVersionId, compressedEdits); } get viewColumn(): vscode.ViewColumn | undefined { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/fold/test/notebookFolding.test.ts b/src/vs/workbench/contrib/notebook/browser/contrib/fold/test/notebookFolding.test.ts index 75400be17fb..3dbd7eadc7b 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/fold/test/notebookFolding.test.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/fold/test/notebookFolding.test.ts @@ -28,13 +28,13 @@ suite('Notebook Folding', () => { blukEditService, undoRedoService, [ - [['# header 1'], 'markdown', CellKind.Markdown, [], {}], - [['body'], 'markdown', CellKind.Markdown, [], {}], - [['## header 2.1'], 'markdown', CellKind.Markdown, [], {}], - [['body 2'], 'markdown', CellKind.Markdown, [], {}], - [['body 3'], 'markdown', CellKind.Markdown, [], {}], - [['## header 2.2'], 'markdown', CellKind.Markdown, [], {}], - [['var e = 7;'], 'markdown', CellKind.Markdown, [], {}], + ['# header 1', 'markdown', CellKind.Markdown, [], {}], + ['body', 'markdown', CellKind.Markdown, [], {}], + ['## header 2.1', 'markdown', CellKind.Markdown, [], {}], + ['body 2', 'markdown', CellKind.Markdown, [], {}], + ['body 3', 'markdown', CellKind.Markdown, [], {}], + ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], + ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], ], (editor, viewModel) => { const foldingController = new FoldingModel(); @@ -57,13 +57,13 @@ suite('Notebook Folding', () => { blukEditService, undoRedoService, [ - [['# header 1'], 'markdown', CellKind.Markdown, [], {}], - [['body'], 'markdown', CellKind.Markdown, [], {}], - [['## header 2.1\n# header3'], 'markdown', CellKind.Markdown, [], {}], - [['body 2'], 'markdown', CellKind.Markdown, [], {}], - [['body 3'], 'markdown', CellKind.Markdown, [], {}], - [['## header 2.2'], 'markdown', CellKind.Markdown, [], {}], - [['var e = 7;'], 'markdown', CellKind.Markdown, [], {}], + ['# header 1', 'markdown', CellKind.Markdown, [], {}], + ['body', 'markdown', CellKind.Markdown, [], {}], + ['## header 2.1\n# header3', 'markdown', CellKind.Markdown, [], {}], + ['body 2', 'markdown', CellKind.Markdown, [], {}], + ['body 3', 'markdown', CellKind.Markdown, [], {}], + ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], + ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], ], (editor, viewModel) => { const foldingController = new FoldingModel(); @@ -91,13 +91,13 @@ suite('Notebook Folding', () => { blukEditService, undoRedoService, [ - [['# header 1'], 'markdown', CellKind.Markdown, [], {}], - [['body'], 'markdown', CellKind.Markdown, [], {}], - [['## header 2.1'], 'markdown', CellKind.Markdown, [], {}], - [['body 2'], 'markdown', CellKind.Markdown, [], {}], - [['body 3'], 'markdown', CellKind.Markdown, [], {}], - [['## header 2.2'], 'markdown', CellKind.Markdown, [], {}], - [['var e = 7;'], 'markdown', CellKind.Markdown, [], {}], + ['# header 1', 'markdown', CellKind.Markdown, [], {}], + ['body', 'markdown', CellKind.Markdown, [], {}], + ['## header 2.1', 'markdown', CellKind.Markdown, [], {}], + ['body 2', 'markdown', CellKind.Markdown, [], {}], + ['body 3', 'markdown', CellKind.Markdown, [], {}], + ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], + ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], ], (editor, viewModel) => { const foldingModel = new FoldingModel(); @@ -115,13 +115,13 @@ suite('Notebook Folding', () => { blukEditService, undoRedoService, [ - [['# header 1'], 'markdown', CellKind.Markdown, [], {}], - [['body'], 'markdown', CellKind.Markdown, [], {}], - [['## header 2.1\n'], 'markdown', CellKind.Markdown, [], {}], - [['body 2'], 'markdown', CellKind.Markdown, [], {}], - [['body 3'], 'markdown', CellKind.Markdown, [], {}], - [['## header 2.2'], 'markdown', CellKind.Markdown, [], {}], - [['var e = 7;'], 'markdown', CellKind.Markdown, [], {}], + ['# header 1', 'markdown', CellKind.Markdown, [], {}], + ['body', 'markdown', CellKind.Markdown, [], {}], + ['## header 2.1\n', 'markdown', CellKind.Markdown, [], {}], + ['body 2', 'markdown', CellKind.Markdown, [], {}], + ['body 3', 'markdown', CellKind.Markdown, [], {}], + ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], + ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], ], (editor, viewModel) => { const foldingModel = new FoldingModel(); @@ -140,13 +140,13 @@ suite('Notebook Folding', () => { blukEditService, undoRedoService, [ - [['# header 1'], 'markdown', CellKind.Markdown, [], {}], - [['body'], 'markdown', CellKind.Markdown, [], {}], - [['# header 2.1\n'], 'markdown', CellKind.Markdown, [], {}], - [['body 2'], 'markdown', CellKind.Markdown, [], {}], - [['body 3'], 'markdown', CellKind.Markdown, [], {}], - [['## header 2.2'], 'markdown', CellKind.Markdown, [], {}], - [['var e = 7;'], 'markdown', CellKind.Markdown, [], {}], + ['# header 1', 'markdown', CellKind.Markdown, [], {}], + ['body', 'markdown', CellKind.Markdown, [], {}], + ['# header 2.1\n', 'markdown', CellKind.Markdown, [], {}], + ['body 2', 'markdown', CellKind.Markdown, [], {}], + ['body 3', 'markdown', CellKind.Markdown, [], {}], + ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], + ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], ], (editor, viewModel) => { const foldingModel = new FoldingModel(); @@ -167,13 +167,13 @@ suite('Notebook Folding', () => { blukEditService, undoRedoService, [ - [['# header 1'], 'markdown', CellKind.Markdown, [], {}], - [['body'], 'markdown', CellKind.Markdown, [], {}], - [['# header 2.1\n'], 'markdown', CellKind.Markdown, [], {}], - [['body 2'], 'markdown', CellKind.Markdown, [], {}], - [['body 3'], 'markdown', CellKind.Markdown, [], {}], - [['## header 2.2'], 'markdown', CellKind.Markdown, [], {}], - [['var e = 7;'], 'markdown', CellKind.Markdown, [], {}], + ['# header 1', 'markdown', CellKind.Markdown, [], {}], + ['body', 'markdown', CellKind.Markdown, [], {}], + ['# header 2.1\n', 'markdown', CellKind.Markdown, [], {}], + ['body 2', 'markdown', CellKind.Markdown, [], {}], + ['body 3', 'markdown', CellKind.Markdown, [], {}], + ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], + ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], ], (editor, viewModel) => { const foldingModel = new FoldingModel(); @@ -224,18 +224,18 @@ suite('Notebook Folding', () => { blukEditService, undoRedoService, [ - [['# header 1'], 'markdown', CellKind.Markdown, [], {}], - [['body'], 'markdown', CellKind.Markdown, [], {}], - [['# header 2.1\n'], 'markdown', CellKind.Markdown, [], {}], - [['body 2'], 'markdown', CellKind.Markdown, [], {}], - [['body 3'], 'markdown', CellKind.Markdown, [], {}], - [['## header 2.2'], 'markdown', CellKind.Markdown, [], {}], - [['var e = 7;'], 'markdown', CellKind.Markdown, [], {}], - [['# header 2.1\n'], 'markdown', CellKind.Markdown, [], {}], - [['body 2'], 'markdown', CellKind.Markdown, [], {}], - [['body 3'], 'markdown', CellKind.Markdown, [], {}], - [['## header 2.2'], 'markdown', CellKind.Markdown, [], {}], - [['var e = 7;'], 'markdown', CellKind.Markdown, [], {}], + ['# header 1', 'markdown', CellKind.Markdown, [], {}], + ['body', 'markdown', CellKind.Markdown, [], {}], + ['# header 2.1\n', 'markdown', CellKind.Markdown, [], {}], + ['body 2', 'markdown', CellKind.Markdown, [], {}], + ['body 3', 'markdown', CellKind.Markdown, [], {}], + ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], + ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], + ['# header 2.1\n', 'markdown', CellKind.Markdown, [], {}], + ['body 2', 'markdown', CellKind.Markdown, [], {}], + ['body 3', 'markdown', CellKind.Markdown, [], {}], + ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], + ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], ], (editor, viewModel) => { const foldingModel = new FoldingModel(); @@ -255,18 +255,18 @@ suite('Notebook Folding', () => { blukEditService, undoRedoService, [ - [['# header 1'], 'markdown', CellKind.Markdown, [], {}], - [['body'], 'markdown', CellKind.Markdown, [], {}], - [['# header 2.1\n'], 'markdown', CellKind.Markdown, [], {}], - [['body 2'], 'markdown', CellKind.Markdown, [], {}], - [['body 3'], 'markdown', CellKind.Markdown, [], {}], - [['## header 2.2'], 'markdown', CellKind.Markdown, [], {}], - [['var e = 7;'], 'markdown', CellKind.Markdown, [], {}], - [['# header 2.1\n'], 'markdown', CellKind.Markdown, [], {}], - [['body 2'], 'markdown', CellKind.Markdown, [], {}], - [['body 3'], 'markdown', CellKind.Markdown, [], {}], - [['## header 2.2'], 'markdown', CellKind.Markdown, [], {}], - [['var e = 7;'], 'markdown', CellKind.Markdown, [], {}], + ['# header 1', 'markdown', CellKind.Markdown, [], {}], + ['body', 'markdown', CellKind.Markdown, [], {}], + ['# header 2.1\n', 'markdown', CellKind.Markdown, [], {}], + ['body 2', 'markdown', CellKind.Markdown, [], {}], + ['body 3', 'markdown', CellKind.Markdown, [], {}], + ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], + ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], + ['# header 2.1\n', 'markdown', CellKind.Markdown, [], {}], + ['body 2', 'markdown', CellKind.Markdown, [], {}], + ['body 3', 'markdown', CellKind.Markdown, [], {}], + ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], + ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], ], (editor, viewModel) => { const foldingModel = new FoldingModel(); @@ -290,18 +290,18 @@ suite('Notebook Folding', () => { blukEditService, undoRedoService, [ - [['# header 1'], 'markdown', CellKind.Markdown, [], {}], - [['body'], 'markdown', CellKind.Markdown, [], {}], - [['# header 2.1\n'], 'markdown', CellKind.Markdown, [], {}], - [['body 2'], 'markdown', CellKind.Markdown, [], {}], - [['body 3'], 'markdown', CellKind.Markdown, [], {}], - [['## header 2.2'], 'markdown', CellKind.Markdown, [], {}], - [['var e = 7;'], 'markdown', CellKind.Markdown, [], {}], - [['# header 2.1\n'], 'markdown', CellKind.Markdown, [], {}], - [['body 2'], 'markdown', CellKind.Markdown, [], {}], - [['body 3'], 'markdown', CellKind.Markdown, [], {}], - [['## header 2.2'], 'markdown', CellKind.Markdown, [], {}], - [['var e = 7;'], 'markdown', CellKind.Markdown, [], {}], + ['# header 1', 'markdown', CellKind.Markdown, [], {}], + ['body', 'markdown', CellKind.Markdown, [], {}], + ['# header 2.1\n', 'markdown', CellKind.Markdown, [], {}], + ['body 2', 'markdown', CellKind.Markdown, [], {}], + ['body 3', 'markdown', CellKind.Markdown, [], {}], + ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], + ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], + ['# header 2.1\n', 'markdown', CellKind.Markdown, [], {}], + ['body 2', 'markdown', CellKind.Markdown, [], {}], + ['body 3', 'markdown', CellKind.Markdown, [], {}], + ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], + ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], ], (editor, viewModel) => { const foldingModel = new FoldingModel(); @@ -327,18 +327,18 @@ suite('Notebook Folding', () => { blukEditService, undoRedoService, [ - [['# header 1'], 'markdown', CellKind.Markdown, [], {}], - [['body'], 'markdown', CellKind.Markdown, [], {}], - [['# header 2.1\n'], 'markdown', CellKind.Markdown, [], {}], - [['body 2'], 'markdown', CellKind.Markdown, [], {}], - [['body 3'], 'markdown', CellKind.Markdown, [], {}], - [['## header 2.2'], 'markdown', CellKind.Markdown, [], {}], - [['var e = 7;'], 'markdown', CellKind.Markdown, [], {}], - [['# header 2.1\n'], 'markdown', CellKind.Markdown, [], {}], - [['body 2'], 'markdown', CellKind.Markdown, [], {}], - [['body 3'], 'markdown', CellKind.Markdown, [], {}], - [['## header 2.2'], 'markdown', CellKind.Markdown, [], {}], - [['var e = 7;'], 'markdown', CellKind.Markdown, [], {}], + ['# header 1', 'markdown', CellKind.Markdown, [], {}], + ['body', 'markdown', CellKind.Markdown, [], {}], + ['# header 2.1\n', 'markdown', CellKind.Markdown, [], {}], + ['body 2', 'markdown', CellKind.Markdown, [], {}], + ['body 3', 'markdown', CellKind.Markdown, [], {}], + ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], + ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], + ['# header 2.1\n', 'markdown', CellKind.Markdown, [], {}], + ['body 2', 'markdown', CellKind.Markdown, [], {}], + ['body 3', 'markdown', CellKind.Markdown, [], {}], + ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], + ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], ], (editor, viewModel) => { const foldingModel = new FoldingModel(); @@ -366,18 +366,18 @@ suite('Notebook Folding', () => { blukEditService, undoRedoService, [ - [['# header 1'], 'markdown', CellKind.Markdown, [], {}], - [['body'], 'markdown', CellKind.Markdown, [], {}], - [['# header 2.1\n'], 'markdown', CellKind.Markdown, [], {}], - [['body 2'], 'markdown', CellKind.Markdown, [], {}], - [['body 3'], 'markdown', CellKind.Markdown, [], {}], - [['## header 2.2'], 'markdown', CellKind.Markdown, [], {}], - [['var e = 7;'], 'markdown', CellKind.Markdown, [], {}], - [['# header 2.1\n'], 'markdown', CellKind.Markdown, [], {}], - [['body 2'], 'markdown', CellKind.Markdown, [], {}], - [['body 3'], 'markdown', CellKind.Markdown, [], {}], - [['## header 2.2'], 'markdown', CellKind.Markdown, [], {}], - [['var e = 7;'], 'markdown', CellKind.Markdown, [], {}], + ['# header 1', 'markdown', CellKind.Markdown, [], {}], + ['body', 'markdown', CellKind.Markdown, [], {}], + ['# header 2.1\n', 'markdown', CellKind.Markdown, [], {}], + ['body 2', 'markdown', CellKind.Markdown, [], {}], + ['body 3', 'markdown', CellKind.Markdown, [], {}], + ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], + ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], + ['# header 2.1\n', 'markdown', CellKind.Markdown, [], {}], + ['body 2', 'markdown', CellKind.Markdown, [], {}], + ['body 3', 'markdown', CellKind.Markdown, [], {}], + ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], + ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], ], (editor, viewModel) => { const foldingModel = new FoldingModel(); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index e7befc90545..44f506a46e5 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1245,7 +1245,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor const insertIndex = cell ? (direction === 'above' ? index : nextIndex) : index; - const newCell = this._notebookViewModel!.createCell(insertIndex, initialText.split(/\r?\n/g), language, type, undefined, true); + const newCell = this._notebookViewModel!.createCell(insertIndex, initialText, language, type, undefined, true); return newCell as CellViewModel; } diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts index 3c26621a682..033f378e21c 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts @@ -609,7 +609,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD return result; } - createCell(index: number, source: string | string[], language: string, type: CellKind, metadata: NotebookCellMetadata | undefined, synchronous: boolean, pushUndoStop: boolean = true) { + createCell(index: number, source: string, language: string, type: CellKind, metadata: NotebookCellMetadata | undefined, synchronous: boolean, pushUndoStop: boolean = true) { this._notebook.createCell2(index, source, language, type, metadata, synchronous, pushUndoStop, undefined, undefined); // TODO, rely on createCell to be sync return this.viewCells[index]; @@ -755,7 +755,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD language, kind, { - createCell: (index: number, source: string | string[], language: string, type: CellKind) => { + createCell: (index: number, source: string, language: string, type: CellKind) => { return this.createCell(index, source, language, type, undefined, true, false) as BaseCellViewModel; }, deleteCell: (index: number) => { diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts index 015f62a96e3..c52f4f5be2a 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts @@ -59,7 +59,7 @@ export class NotebookCellTextModel extends Disposable implements ICell { } const builder = new PieceTreeTextBufferBuilder(); - builder.acceptChunk(Array.isArray(this._source) ? this._source.join('\n') : this._source); + builder.acceptChunk(this._source); const bufferFactory = builder.finish(true); this._textBuffer = bufferFactory.create(model.DefaultEndOfLine.LF); @@ -74,7 +74,7 @@ export class NotebookCellTextModel extends Disposable implements ICell { constructor( readonly uri: URI, public handle: number, - private _source: string | string[], + private _source: string, private _language: string, public cellKind: CellKind, outputs: IProcessedOutput[], diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 920d34f6f70..8c669ea4487 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -186,7 +186,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel } createCellTextModel( - source: string | string[], + source: string, language: string, cellKind: CellKind, outputs: IProcessedOutput[], @@ -559,7 +559,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._emitSelections.fire(selections); } - createCell2(index: number, source: string | string[], language: string, type: CellKind, metadata: NotebookCellMetadata | undefined, synchronous: boolean, pushUndoStop: boolean, beforeSelections: number[] | undefined, endSelections: number[] | undefined) { + createCell2(index: number, source: string, language: string, type: CellKind, metadata: NotebookCellMetadata | undefined, synchronous: boolean, pushUndoStop: boolean, beforeSelections: number[] | undefined, endSelections: number[] | undefined) { const cell = this.createCellTextModel(source, language, type, [], metadata); if (pushUndoStop) { diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 4ae28b0e979..bd981a009b3 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -404,7 +404,7 @@ export enum CellEditType { } export interface ICellDto2 { - source: string | string[]; + source: string; language: string; cellKind: CellKind; outputs: IProcessedOutput[]; @@ -428,7 +428,6 @@ export type ICellEditOperation = ICellInsertEdit | ICellDeleteEdit; export interface INotebookEditData { documentVersionId: number; edits: ICellEditOperation[]; - renderers: number[]; } export interface NotebookDataDto { diff --git a/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts b/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts index d092389a66e..38a91a236ce 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts @@ -269,7 +269,7 @@ suite('NotebookCommon', () => { for (let i = 0; i < 5; i++) { cells.push( - new TestCell('notebook', i, [`var a = ${i};`], 'javascript', CellKind.Code, [], textModelService) + new TestCell('notebook', i, `var a = ${i};`, 'javascript', CellKind.Code, [], textModelService) ); } @@ -295,8 +295,8 @@ suite('NotebookCommon', () => { ] ); - const cellA = new TestCell('notebook', 6, ['var a = 6;'], 'javascript', CellKind.Code, [], textModelService); - const cellB = new TestCell('notebook', 7, ['var a = 7;'], 'javascript', CellKind.Code, [], textModelService); + const cellA = new TestCell('notebook', 6, 'var a = 6;', 'javascript', CellKind.Code, [], textModelService); + const cellB = new TestCell('notebook', 7, 'var a = 7;', 'javascript', CellKind.Code, [], textModelService); const modifiedCells = [ cells[0], diff --git a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts index 23ffd1fb439..892e9677c2f 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts @@ -23,15 +23,15 @@ suite('NotebookTextModel', () => { blukEditService, undoRedoService, [ - [['var a = 1;'], 'javascript', CellKind.Code, [], { editable: true }], - [['var b = 2;'], 'javascript', CellKind.Code, [], { editable: false }], - [['var c = 3;'], 'javascript', CellKind.Code, [], { editable: false }], - [['var d = 4;'], 'javascript', CellKind.Code, [], { editable: false }] + ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], + ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }], + ['var c = 3;', 'javascript', CellKind.Code, [], { editable: false }], + ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] ], (editor, viewModel, textModel) => { textModel.$applyEdit(textModel.versionId, [ - { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [], textModelService)] }, - { editType: CellEditType.Insert, index: 3, cells: [new TestCell(viewModel.viewType, 6, ['var f = 6;'], 'javascript', CellKind.Code, [], textModelService)] }, + { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], textModelService)] }, + { editType: CellEditType.Insert, index: 3, cells: [new TestCell(viewModel.viewType, 6, 'var f = 6;', 'javascript', CellKind.Code, [], textModelService)] }, ], true); assert.equal(textModel.cells.length, 6); @@ -48,15 +48,15 @@ suite('NotebookTextModel', () => { blukEditService, undoRedoService, [ - [['var a = 1;'], 'javascript', CellKind.Code, [], { editable: true }], - [['var b = 2;'], 'javascript', CellKind.Code, [], { editable: false }], - [['var c = 3;'], 'javascript', CellKind.Code, [], { editable: false }], - [['var d = 4;'], 'javascript', CellKind.Code, [], { editable: false }] + ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], + ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }], + ['var c = 3;', 'javascript', CellKind.Code, [], { editable: false }], + ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] ], (editor, viewModel, textModel) => { textModel.$applyEdit(textModel.versionId, [ - { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [], textModelService)] }, - { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 6, ['var f = 6;'], 'javascript', CellKind.Code, [], textModelService)] }, + { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], textModelService)] }, + { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 6, 'var f = 6;', 'javascript', CellKind.Code, [], textModelService)] }, ], true); assert.equal(textModel.cells.length, 6); @@ -73,10 +73,10 @@ suite('NotebookTextModel', () => { blukEditService, undoRedoService, [ - [['var a = 1;'], 'javascript', CellKind.Code, [], { editable: true }], - [['var b = 2;'], 'javascript', CellKind.Code, [], { editable: false }], - [['var c = 3;'], 'javascript', CellKind.Code, [], { editable: false }], - [['var d = 4;'], 'javascript', CellKind.Code, [], { editable: false }] + ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], + ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }], + ['var c = 3;', 'javascript', CellKind.Code, [], { editable: false }], + ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] ], (editor, viewModel, textModel) => { textModel.$applyEdit(textModel.versionId, [ @@ -96,15 +96,15 @@ suite('NotebookTextModel', () => { blukEditService, undoRedoService, [ - [['var a = 1;'], 'javascript', CellKind.Code, [], { editable: true }], - [['var b = 2;'], 'javascript', CellKind.Code, [], { editable: false }], - [['var c = 3;'], 'javascript', CellKind.Code, [], { editable: false }], - [['var d = 4;'], 'javascript', CellKind.Code, [], { editable: false }] + ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], + ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }], + ['var c = 3;', 'javascript', CellKind.Code, [], { editable: false }], + ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] ], (editor, viewModel, textModel) => { textModel.$applyEdit(textModel.versionId, [ { editType: CellEditType.Delete, index: 1, count: 1 }, - { editType: CellEditType.Insert, index: 3, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [], textModelService)] }, + { editType: CellEditType.Insert, index: 3, cells: [new TestCell(viewModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], textModelService)] }, ], true); assert.equal(textModel.cells.length, 4); @@ -121,15 +121,15 @@ suite('NotebookTextModel', () => { blukEditService, undoRedoService, [ - [['var a = 1;'], 'javascript', CellKind.Code, [], { editable: true }], - [['var b = 2;'], 'javascript', CellKind.Code, [], { editable: false }], - [['var c = 3;'], 'javascript', CellKind.Code, [], { editable: false }], - [['var d = 4;'], 'javascript', CellKind.Code, [], { editable: false }] + ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], + ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }], + ['var c = 3;', 'javascript', CellKind.Code, [], { editable: false }], + ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] ], (editor, viewModel, textModel) => { textModel.$applyEdit(textModel.versionId, [ { editType: CellEditType.Delete, index: 1, count: 1 }, - { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [], textModelService)] }, + { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], textModelService)] }, ], true); assert.equal(textModel.cells.length, 4); @@ -140,4 +140,3 @@ suite('NotebookTextModel', () => { ); }); }); - diff --git a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts index 052842994fa..70d9b950c3f 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts @@ -36,14 +36,14 @@ suite('NotebookViewModel', () => { blukEditService, undoRedoService, [ - [['var a = 1;'], 'javascript', CellKind.Code, [], { editable: true }], - [['var b = 2;'], 'javascript', CellKind.Code, [], { editable: false }] + ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], + ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }] ], (editor, viewModel) => { assert.equal(viewModel.viewCells[0].metadata?.editable, true); assert.equal(viewModel.viewCells[1].metadata?.editable, false); - const cell = viewModel.insertCell(1, new TestCell(viewModel.viewType, 0, ['var c = 3;'], 'javascript', CellKind.Code, [], textModelService), true); + const cell = viewModel.insertCell(1, new TestCell(viewModel.viewType, 0, 'var c = 3;', 'javascript', CellKind.Code, [], textModelService), true); assert.equal(viewModel.viewCells.length, 3); assert.equal(viewModel.notebookDocument.cells.length, 3); assert.equal(viewModel.getCellIndex(cell), 1); @@ -62,9 +62,9 @@ suite('NotebookViewModel', () => { blukEditService, undoRedoService, [ - [['//a'], 'javascript', CellKind.Code, [], { editable: true }], - [['//b'], 'javascript', CellKind.Code, [], { editable: true }], - [['//c'], 'javascript', CellKind.Code, [], { editable: true }], + ['//a', 'javascript', CellKind.Code, [], { editable: true }], + ['//b', 'javascript', CellKind.Code, [], { editable: true }], + ['//c', 'javascript', CellKind.Code, [], { editable: true }], ], (editor, viewModel) => { viewModel.moveCellToIdx(0, 1, 0, false); @@ -93,9 +93,9 @@ suite('NotebookViewModel', () => { blukEditService, undoRedoService, [ - [['//a'], 'javascript', CellKind.Code, [], { editable: true }], - [['//b'], 'javascript', CellKind.Code, [], { editable: true }], - [['//c'], 'javascript', CellKind.Code, [], { editable: true }], + ['//a', 'javascript', CellKind.Code, [], { editable: true }], + ['//b', 'javascript', CellKind.Code, [], { editable: true }], + ['//c', 'javascript', CellKind.Code, [], { editable: true }], ], (editor, viewModel) => { viewModel.moveCellToIdx(1, 1, 0, false); @@ -118,21 +118,21 @@ suite('NotebookViewModel', () => { blukEditService, undoRedoService, [ - [['var a = 1;'], 'javascript', CellKind.Code, [], { editable: true }], - [['var b = 2;'], 'javascript', CellKind.Code, [], { editable: true }] + ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], + ['var b = 2;', 'javascript', CellKind.Code, [], { editable: true }] ], (editor, viewModel) => { const firstViewCell = viewModel.viewCells[0]; const lastViewCell = viewModel.viewCells[viewModel.viewCells.length - 1]; const insertIndex = viewModel.getCellIndex(firstViewCell) + 1; - const cell = viewModel.insertCell(insertIndex, new TestCell(viewModel.viewType, 3, ['var c = 3;'], 'javascript', CellKind.Code, [], textModelService), true); + const cell = viewModel.insertCell(insertIndex, new TestCell(viewModel.viewType, 3, 'var c = 3;', 'javascript', CellKind.Code, [], textModelService), true); const addedCellIndex = viewModel.getCellIndex(cell); viewModel.deleteCell(addedCellIndex, true); const secondInsertIndex = viewModel.getCellIndex(lastViewCell) + 1; - const cell2 = viewModel.insertCell(secondInsertIndex, new TestCell(viewModel.viewType, 4, ['var d = 4;'], 'javascript', CellKind.Code, [], textModelService), true); + const cell2 = viewModel.insertCell(secondInsertIndex, new TestCell(viewModel.viewType, 4, 'var d = 4;', 'javascript', CellKind.Code, [], textModelService), true); assert.equal(viewModel.viewCells.length, 3); assert.equal(viewModel.notebookDocument.cells.length, 3); @@ -147,11 +147,11 @@ suite('NotebookViewModel', () => { blukEditService, undoRedoService, [ - [['var a = 1;'], 'javascript', CellKind.Code, [], {}], - [['var b = 2;'], 'javascript', CellKind.Code, [], { editable: true, runnable: true }], - [['var c = 3;'], 'javascript', CellKind.Code, [], { editable: true, runnable: false }], - [['var d = 4;'], 'javascript', CellKind.Code, [], { editable: false, runnable: true }], - [['var e = 5;'], 'javascript', CellKind.Code, [], { editable: false, runnable: false }], + ['var a = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], { editable: true, runnable: true }], + ['var c = 3;', 'javascript', CellKind.Code, [], { editable: true, runnable: false }], + ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false, runnable: true }], + ['var e = 5;', 'javascript', CellKind.Code, [], { editable: false, runnable: false }], ], (editor, viewModel) => { viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: true, cellEditable: true, cellHasExecutionOrder: true }; @@ -269,11 +269,11 @@ suite('NotebookViewModel Decorations', () => { blukEditService, undoRedoService, [ - [['var a = 1;'], 'javascript', CellKind.Code, [], {}], - [['var b = 2;'], 'javascript', CellKind.Code, [], { editable: true, runnable: true }], - [['var c = 3;'], 'javascript', CellKind.Code, [], { editable: true, runnable: false }], - [['var d = 4;'], 'javascript', CellKind.Code, [], { editable: false, runnable: true }], - [['var e = 5;'], 'javascript', CellKind.Code, [], { editable: false, runnable: false }], + ['var a = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], { editable: true, runnable: true }], + ['var c = 3;', 'javascript', CellKind.Code, [], { editable: true, runnable: false }], + ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false, runnable: true }], + ['var e = 5;', 'javascript', CellKind.Code, [], { editable: false, runnable: false }], ], (editor, viewModel) => { const trackedId = viewModel.setTrackedRange('test', { start: 1, end: 2 }, TrackedRangeStickiness.GrowsOnlyWhenTypingAfter); @@ -283,7 +283,7 @@ suite('NotebookViewModel Decorations', () => { end: 2, }); - viewModel.insertCell(0, new TestCell(viewModel.viewType, 5, ['var d = 6;'], 'javascript', CellKind.Code, [], textModelService), true); + viewModel.insertCell(0, new TestCell(viewModel.viewType, 5, 'var d = 6;', 'javascript', CellKind.Code, [], textModelService), true); assert.deepEqual(viewModel.getTrackedRange(trackedId!), { start: 2, @@ -297,7 +297,7 @@ suite('NotebookViewModel Decorations', () => { end: 2 }); - viewModel.insertCell(3, new TestCell(viewModel.viewType, 6, ['var d = 7;'], 'javascript', CellKind.Code, [], textModelService), true); + viewModel.insertCell(3, new TestCell(viewModel.viewType, 6, 'var d = 7;', 'javascript', CellKind.Code, [], textModelService), true); assert.deepEqual(viewModel.getTrackedRange(trackedId!), { start: 1, @@ -327,13 +327,13 @@ suite('NotebookViewModel Decorations', () => { blukEditService, undoRedoService, [ - [['var a = 1;'], 'javascript', CellKind.Code, [], {}], - [['var b = 2;'], 'javascript', CellKind.Code, [], { editable: true, runnable: true }], - [['var c = 3;'], 'javascript', CellKind.Code, [], { editable: true, runnable: false }], - [['var d = 4;'], 'javascript', CellKind.Code, [], { editable: false, runnable: true }], - [['var e = 5;'], 'javascript', CellKind.Code, [], { editable: false, runnable: false }], - [['var e = 6;'], 'javascript', CellKind.Code, [], { editable: false, runnable: false }], - [['var e = 7;'], 'javascript', CellKind.Code, [], { editable: false, runnable: false }], + ['var a = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], { editable: true, runnable: true }], + ['var c = 3;', 'javascript', CellKind.Code, [], { editable: true, runnable: false }], + ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false, runnable: true }], + ['var e = 5;', 'javascript', CellKind.Code, [], { editable: false, runnable: false }], + ['var e = 6;', 'javascript', CellKind.Code, [], { editable: false, runnable: false }], + ['var e = 7;', 'javascript', CellKind.Code, [], { editable: false, runnable: false }], ], (editor, viewModel) => { const trackedId = viewModel.setTrackedRange('test', { start: 1, end: 3 }, TrackedRangeStickiness.GrowsOnlyWhenTypingAfter); @@ -343,14 +343,14 @@ suite('NotebookViewModel Decorations', () => { end: 3 }); - viewModel.insertCell(5, new TestCell(viewModel.viewType, 8, ['var d = 9;'], 'javascript', CellKind.Code, [], textModelService), true); + viewModel.insertCell(5, new TestCell(viewModel.viewType, 8, 'var d = 9;', 'javascript', CellKind.Code, [], textModelService), true); assert.deepEqual(viewModel.getTrackedRange(trackedId!), { start: 1, end: 3 }); - viewModel.insertCell(4, new TestCell(viewModel.viewType, 9, ['var d = 10;'], 'javascript', CellKind.Code, [], textModelService), true); + viewModel.insertCell(4, new TestCell(viewModel.viewType, 9, 'var d = 10;', 'javascript', CellKind.Code, [], textModelService), true); assert.deepEqual(viewModel.getTrackedRange(trackedId!), { start: 1, diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index 98d72f689d3..eebd1a26cf0 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -39,7 +39,7 @@ export class TestCell extends NotebookCellTextModel { constructor( public viewType: string, handle: number, - public source: string[], + public source: string, language: string, cellKind: CellKind, outputs: IProcessedOutput[], @@ -375,7 +375,7 @@ export function setupInstantiationService() { return instantiationService; } -export function withTestNotebook(instantiationService: TestInstantiationService, blukEditService: IBulkEditService, undoRedoService: IUndoRedoService, cells: [string[], string, CellKind, IProcessedOutput[], NotebookCellMetadata][], callback: (editor: TestNotebookEditor, viewModel: NotebookViewModel, textModel: NotebookTextModel) => void) { +export function withTestNotebook(instantiationService: TestInstantiationService, blukEditService: IBulkEditService, undoRedoService: IUndoRedoService, cells: [string, string, CellKind, IProcessedOutput[], NotebookCellMetadata][], callback: (editor: TestNotebookEditor, viewModel: NotebookViewModel, textModel: NotebookTextModel) => void) { const textModelService = instantiationService.get(ITextModelService); const viewType = 'notebook'; From db1f9460733c5e3b64647db9025438cf42473b95 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 24 Aug 2020 11:08:22 +0200 Subject: [PATCH 458/736] chore - use $-prefix for IPC call onlys, fyi @rebornix --- src/vs/workbench/api/browser/mainThreadNotebook.ts | 6 +++--- .../contrib/notebook/common/model/notebookTextModel.ts | 4 ++-- .../contrib/notebook/test/notebookTextModel.test.ts | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index c767266373d..86bbeb4083e 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -194,7 +194,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); if (textModel) { this._notebookService.transformEditsOutputs(textModel, edits); - return textModel.$applyEdit(modelVersionId, edits, true); + return textModel.applyEdit(modelVersionId, edits, true); } return false; @@ -434,7 +434,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo this._notebookService.transformEditsOutputs(mainthreadTextModel, edits); await new Promise(resolve => { DOM.scheduleAtNextAnimationFrame(() => { - const ret = mainthreadTextModel!.$applyEdit(mainthreadTextModel!.versionId, edits, true); + const ret = mainthreadTextModel!.applyEdit(mainthreadTextModel!.versionId, edits, true); resolve(ret); }); }); @@ -626,7 +626,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); if (textModel) { - textModel.$handleEdit(label, () => { + textModel.handleEdit(label, () => { return this._proxy.$undoNotebook(textModel.viewType, textModel.uri, editId, textModel.isDirty); }, () => { return this._proxy.$redoNotebook(textModel.viewType, textModel.uri, editId, textModel.isDirty); diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 8c669ea4487..a47a5fc4f0c 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -227,7 +227,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._operationManager.pushStackElement(label); } - $applyEdit(modelVersionId: number, rawEdits: ICellEditOperation[], synchronous: boolean): boolean { + applyEdit(modelVersionId: number, rawEdits: ICellEditOperation[], synchronous: boolean): boolean { if (modelVersionId !== this._versionId) { return false; } @@ -319,7 +319,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel return true; } - $handleEdit(label: string | undefined, undo: () => void, redo: () => void): void { + handleEdit(label: string | undefined, undo: () => void, redo: () => void): void { this._operationManager.pushEditOperation({ type: UndoRedoElementType.Resource, resource: this.uri, diff --git a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts index 892e9677c2f..b0e9bf0424e 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts @@ -29,7 +29,7 @@ suite('NotebookTextModel', () => { ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] ], (editor, viewModel, textModel) => { - textModel.$applyEdit(textModel.versionId, [ + textModel.applyEdit(textModel.versionId, [ { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], textModelService)] }, { editType: CellEditType.Insert, index: 3, cells: [new TestCell(viewModel.viewType, 6, 'var f = 6;', 'javascript', CellKind.Code, [], textModelService)] }, ], true); @@ -54,7 +54,7 @@ suite('NotebookTextModel', () => { ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] ], (editor, viewModel, textModel) => { - textModel.$applyEdit(textModel.versionId, [ + textModel.applyEdit(textModel.versionId, [ { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], textModelService)] }, { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 6, 'var f = 6;', 'javascript', CellKind.Code, [], textModelService)] }, ], true); @@ -79,7 +79,7 @@ suite('NotebookTextModel', () => { ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] ], (editor, viewModel, textModel) => { - textModel.$applyEdit(textModel.versionId, [ + textModel.applyEdit(textModel.versionId, [ { editType: CellEditType.Delete, index: 1, count: 1 }, { editType: CellEditType.Delete, index: 3, count: 1 }, ], true); @@ -102,7 +102,7 @@ suite('NotebookTextModel', () => { ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] ], (editor, viewModel, textModel) => { - textModel.$applyEdit(textModel.versionId, [ + textModel.applyEdit(textModel.versionId, [ { editType: CellEditType.Delete, index: 1, count: 1 }, { editType: CellEditType.Insert, index: 3, cells: [new TestCell(viewModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], textModelService)] }, ], true); @@ -127,7 +127,7 @@ suite('NotebookTextModel', () => { ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] ], (editor, viewModel, textModel) => { - textModel.$applyEdit(textModel.versionId, [ + textModel.applyEdit(textModel.versionId, [ { editType: CellEditType.Delete, index: 1, count: 1 }, { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], textModelService)] }, ], true); From ab8cf002d24a06f4fd54b8c496276e74af6ebc9a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 24 Aug 2020 11:44:12 +0200 Subject: [PATCH 459/736] chore - remove unused code fyi @rebornix --- .../api/browser/mainThreadNotebook.ts | 38 ------------------- 1 file changed, 38 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 86bbeb4083e..45408dcc3d4 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -18,47 +18,9 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IRelativePattern } from 'vs/base/common/glob'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { Emitter } from 'vs/base/common/event'; import { ILogService } from 'vs/platform/log/common/log'; -export class MainThreadNotebookDocument extends Disposable { - private _textModel: NotebookTextModel; - - get textModel() { - return this._textModel; - } - - constructor( - private readonly _proxy: ExtHostNotebookShape, - public handle: number, - public viewType: string, - public supportBackup: boolean, - public uri: URI, - @INotebookService readonly notebookService: INotebookService, - @IUndoRedoService readonly undoRedoService: IUndoRedoService, - @ITextModelService modelService: ITextModelService - - ) { - super(); - - this._textModel = new NotebookTextModel(handle, viewType, supportBackup, uri, undoRedoService, modelService); - this._register(this._textModel.onDidModelChangeProxy(e => { - this._proxy.$acceptModelChanged(this.uri, e); - this._proxy.$acceptEditorPropertiesChanged(uri, { selections: { selections: this._textModel.selections }, metadata: null }); - })); - this._register(this._textModel.onDidSelectionChange(e => { - const selectionsChange = e ? { selections: e } : null; - this._proxy.$acceptEditorPropertiesChanged(uri, { selections: selectionsChange, metadata: null }); - })); - } - - dispose() { - // this._textModel.dispose(); - super.dispose(); - } -} class DocumentAndEditorState { static ofSets(before: Set, after: Set): { removed: T[], added: T[] } { From 88f93ee247ebca77b4e500229b3a0eab02ae0583 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 24 Aug 2020 11:50:16 +0200 Subject: [PATCH 460/736] :lipstick: use resource map --- .../notebook/browser/notebookServiceImpl.ts | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 2fab79b87ab..78a2b098a9b 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -9,6 +9,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import * as glob from 'vs/base/common/glob'; import { Iterable } from 'vs/base/common/iterator'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { ResourceMap } from 'vs/base/common/map'; import { basename } from 'vs/base/common/path'; import { URI } from 'vs/base/common/uri'; import { RedoCommand, UndoCommand } from 'vs/editor/browser/editorExtensions'; @@ -35,10 +36,6 @@ import { ICustomEditorInfo, ICustomEditorViewTypesHandler, IEditorService } from import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -function MODEL_ID(resource: URI): string { - return resource.toString(); -} - export class NotebookKernelProviderInfoStore extends Disposable { private readonly _notebookKernelProviders: INotebookKernelProvider[] = []; @@ -236,7 +233,7 @@ export class NotebookService extends Disposable implements INotebookService, ICu notebookProviderInfoStore: NotebookProviderInfoStore; notebookRenderersInfoStore: NotebookOutputRendererInfoStore = new NotebookOutputRendererInfoStore(); notebookKernelProviderInfoStore: NotebookKernelProviderInfoStore = new NotebookKernelProviderInfoStore(); - private readonly _models = new Map(); + private readonly _models = new ResourceMap(); private _onDidChangeActiveEditor = new Emitter(); onDidChangeActiveEditor: Event = this._onDidChangeActiveEditor.event; private _activeEditorDisposables = new DisposableStore(); @@ -664,12 +661,10 @@ export class NotebookService extends Disposable implements INotebookService, ICu return undefined; } - const modelId = MODEL_ID(uri); - let notebookModel: NotebookTextModel | undefined = undefined; - if (this._models.has(modelId)) { + if (this._models.has(uri)) { // the model already exists - notebookModel = this._models.get(modelId)!.model; + notebookModel = this._models.get(uri)!.model; if (forceReload) { await provider.controller.reloadNotebook(notebookModel); } @@ -690,7 +685,7 @@ export class NotebookService extends Disposable implements INotebookService, ICu (model) => this._onWillDisposeDocument(model), ); - this._models.set(modelId, modelData); + this._models.set(uri, modelData); this._onNotebookDocumentAdd.fire([notebookModel!.uri]); // after the document is added to the store and sent to ext host, we transform the ouputs await this.transformTextModelOutputs(notebookModel!); @@ -703,9 +698,7 @@ export class NotebookService extends Disposable implements INotebookService, ICu } getNotebookTextModel(uri: URI): NotebookTextModel | undefined { - const modelId = MODEL_ID(uri); - - return this._models.get(modelId)?.model; + return this._models.get(uri)?.model; } private async transformTextModelOutputs(textModel: NotebookTextModel) { @@ -1000,10 +993,9 @@ export class NotebookService extends Disposable implements INotebookService, ICu } private _onWillDisposeDocument(model: INotebookTextModel): void { - const modelId = MODEL_ID(model.uri); - const modelData = this._models.get(modelId); - this._models.delete(modelId); + const modelData = this._models.get(model.uri); + this._models.delete(model.uri); if (modelData) { // delete editors and documents From 76c817a5afab8f4afdc0746e7d7eb12c46103d63 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 24 Aug 2020 12:49:40 +0200 Subject: [PATCH 461/736] Fixes #104937: Render decorations in minimap if semantic occurrences highlighting is disabled --- src/vs/editor/contrib/multicursor/multicursor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/multicursor/multicursor.ts b/src/vs/editor/contrib/multicursor/multicursor.ts index 4bf81a1ec8a..6a7c295e827 100644 --- a/src/vs/editor/contrib/multicursor/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/multicursor.ts @@ -971,7 +971,7 @@ export class SelectionHighlighter extends Disposable implements IEditorContribut return; } - const hasFindOccurrences = DocumentHighlightProviderRegistry.has(model); + const hasFindOccurrences = DocumentHighlightProviderRegistry.has(model) && this.editor.getOption(EditorOption.occurrencesHighlight); let allMatches = model.findMatches(this.state.searchText, true, false, this.state.matchCase, this.state.wordSeparators, false).map(m => m.range); allMatches.sort(Range.compareRangesUsingStarts); From bc9f2577cd8e297b003e5ca652e19685504a1e50 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 24 Aug 2020 14:31:40 +0200 Subject: [PATCH 462/736] Fixes #105152: Have the default word navigation to the left be similar to the defailt word navigation to the right --- .../contrib/wordOperations/wordOperations.ts | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/vs/editor/contrib/wordOperations/wordOperations.ts b/src/vs/editor/contrib/wordOperations/wordOperations.ts index 791e831aab0..2e2b7013238 100644 --- a/src/vs/editor/contrib/wordOperations/wordOperations.ts +++ b/src/vs/editor/contrib/wordOperations/wordOperations.ts @@ -101,13 +101,7 @@ export class CursorWordStartLeft extends WordLeftCommand { inSelectionMode: false, wordNavigationType: WordNavigationType.WordStart, id: 'cursorWordStartLeft', - precondition: undefined, - kbOpts: { - kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.CtrlCmd | KeyCode.LeftArrow, - mac: { primary: KeyMod.Alt | KeyCode.LeftArrow }, - weight: KeybindingWeight.EditorContrib - } + precondition: undefined }); } } @@ -129,7 +123,13 @@ export class CursorWordLeft extends WordLeftCommand { inSelectionMode: false, wordNavigationType: WordNavigationType.WordStartFast, id: 'cursorWordLeft', - precondition: undefined + precondition: undefined, + kbOpts: { + kbExpr: EditorContextKeys.textInputFocus, + primary: KeyMod.CtrlCmd | KeyCode.LeftArrow, + mac: { primary: KeyMod.Alt | KeyCode.LeftArrow }, + weight: KeybindingWeight.EditorContrib + } }); } } @@ -140,13 +140,7 @@ export class CursorWordStartLeftSelect extends WordLeftCommand { inSelectionMode: true, wordNavigationType: WordNavigationType.WordStart, id: 'cursorWordStartLeftSelect', - precondition: undefined, - kbOpts: { - kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow, - mac: { primary: KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow }, - weight: KeybindingWeight.EditorContrib - } + precondition: undefined }); } } @@ -168,7 +162,13 @@ export class CursorWordLeftSelect extends WordLeftCommand { inSelectionMode: true, wordNavigationType: WordNavigationType.WordStartFast, id: 'cursorWordLeftSelect', - precondition: undefined + precondition: undefined, + kbOpts: { + kbExpr: EditorContextKeys.textInputFocus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow, + mac: { primary: KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow }, + weight: KeybindingWeight.EditorContrib + } }); } } From ac4de7bbb3f4a2323f32e11db4bd163e61998e49 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 24 Aug 2020 15:19:12 +0200 Subject: [PATCH 463/736] add NotebookEditorCellEdit#replaceOutputs, https://github.com/microsoft/vscode/issues/105283 --- src/vs/vscode.proposed.d.ts | 2 ++ src/vs/workbench/api/browser/mainThreadNotebook.ts | 2 +- src/vs/workbench/api/common/extHostNotebook.ts | 9 +++++++++ .../notebook/browser/notebookServiceImpl.ts | 10 ++++++++++ .../notebook/common/model/notebookTextModel.ts | 9 +++++++-- .../contrib/notebook/common/notebookCommon.ts | 14 +++++++++++--- 6 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 3a1ad120a9d..d1b72cac46f 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1357,6 +1357,8 @@ declare module 'vscode' { replaceCells(from: number, to: number, cells: NotebookCellData[]): void; + replaceOutputs(index: number, outputs: CellOutput[]): void; + /** @deprecated */ insert(index: number, content: string | string[], language: string, type: CellKind, outputs: CellOutput[], metadata: NotebookCellMetadata | undefined): void; /** @deprecated */ diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 45408dcc3d4..a1acb30628d 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -560,7 +560,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo if (textModel) { this._notebookService.transformSpliceOutputs(textModel, splices); - textModel.$spliceNotebookCellOutputs(cellHandle, splices); + textModel.spliceNotebookCellOutputs(cellHandle, splices); } } diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 22c52d2e7ed..a2a995327a2 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -511,6 +511,15 @@ export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellE } } + replaceOutputs(index: number, outputs: vscode.CellOutput[]): void { + this._throwIfFinalized(); + this._collectedEdits.push({ + editType: CellEditType.Output, + index, + outputs: outputs.map(output => addIdToOutput(output)) + }); + } + replaceCells(from: number, to: number, cells: vscode.NotebookCellData[]): void { this._throwIfFinalized(); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 78a2b098a9b..5a72f03823c 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -733,6 +733,16 @@ export class NotebookService extends Disposable implements INotebookService, ICu } }); }); + } else if (edit.editType === CellEditType.Output) { + edit.outputs.map((output) => { + if (output.outputKind === CellOutputKind.Rich) { + const ret = this._transformMimeTypes(output, output.outputId, textModel.metadata.displayOrder as string[] || []); + const orderedMimeTypes = ret.orderedMimeTypes!; + const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!; + output.pickedMimeTypeIndex = pickedMimeTypeIndex; + output.orderedMimeTypes = orderedMimeTypes; + } + }); } }); } diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index a47a5fc4f0c..272b1ba7c29 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -8,7 +8,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { INotebookTextModel, NotebookCellOutputsSplice, NotebookCellTextModelSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, ICellInsertEdit, NotebookCellsChangedEvent, CellKind, IProcessedOutput, notebookDocumentMetadataDefaults, diff, ICellDeleteEdit, NotebookCellsChangeType, ICellDto2, IMainCellDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookTextModel, NotebookCellOutputsSplice, NotebookCellTextModelSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, ICellInsertEdit, NotebookCellsChangedEvent, CellKind, IProcessedOutput, notebookDocumentMetadataDefaults, diff, ICellDeleteEdit, NotebookCellsChangeType, ICellDto2, IMainCellDto, ICellOutputEdit } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ITextSnapshot } from 'vs/editor/common/model'; import { IUndoRedoService, UndoRedoElementType, IUndoRedoElement, IResourceUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo'; import { InsertCellEdit, DeleteCellEdit, MoveCellEdit, SpliceCellsEdit } from 'vs/workbench/contrib/notebook/common/model/cellEdit'; @@ -279,6 +279,11 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel case CellEditType.Delete: this.removeCell(operations[i].index, operations[i].end - operations[i].start, false); break; + case CellEditType.Output: + //TODO@joh,@rebornix no event, no undo stop (?) + const cell = this.cells[operations[i].index]; + this.spliceNotebookCellOutputs(cell.handle, [[0, cell.outputs.length, (operations[i]).outputs]]); + break; } } @@ -493,7 +498,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel } // TODO@rebornix should this trigger content change event? - $spliceNotebookCellOutputs(cellHandle: number, splices: NotebookCellOutputsSplice[]): void { + spliceNotebookCellOutputs(cellHandle: number, splices: NotebookCellOutputsSplice[]): void { const cell = this._mapping.get(cellHandle); cell?.spliceNotebookCellOutputs(splices); } diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index bd981a009b3..ff722330fc1 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -398,9 +398,11 @@ export interface NotebookCellsChangeMetadataEvent { } export type NotebookCellsChangedEvent = NotebookCellsInitializeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookCellClearOutputEvent | NotebookCellsClearOutputEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent; -export enum CellEditType { + +export const enum CellEditType { Insert = 1, - Delete = 2 + Delete = 2, + Output = 3, } export interface ICellDto2 { @@ -423,7 +425,13 @@ export interface ICellDeleteEdit { count: number; } -export type ICellEditOperation = ICellInsertEdit | ICellDeleteEdit; +export interface ICellOutputEdit { + editType: CellEditType.Output; + index: number, + outputs: IProcessedOutput[] +} + +export type ICellEditOperation = ICellInsertEdit | ICellDeleteEdit | ICellOutputEdit; export interface INotebookEditData { documentVersionId: number; From f3ac25fdd0d57cd22e8e964164a071550b226b30 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 24 Aug 2020 06:19:03 -0700 Subject: [PATCH 464/736] Support .utf8 format in LANG Fixes #105206 --- .../contrib/terminal/common/terminalEnvironment.ts | 2 +- .../contrib/terminal/test/node/terminalEnvironment.test.ts | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts index 20c3551c8f7..2ad5db5eb26 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts @@ -93,7 +93,7 @@ export function shouldSetLangEnvVariable(env: platform.IProcessEnvironment, dete return true; } if (detectLocale === 'auto') { - return !env['LANG'] || env['LANG'].search(/\.UTF\-8$/) === -1; + return !env['LANG'] || (env['LANG'].search(/\.UTF\-8$/) === -1 && env['LANG'].search(/\.utf8$/) === -1); } return false; // 'off' } diff --git a/src/vs/workbench/contrib/terminal/test/node/terminalEnvironment.test.ts b/src/vs/workbench/contrib/terminal/test/node/terminalEnvironment.test.ts index 78d6cee648e..2ced826aa57 100644 --- a/src/vs/workbench/contrib/terminal/test/node/terminalEnvironment.test.ts +++ b/src/vs/workbench/contrib/terminal/test/node/terminalEnvironment.test.ts @@ -45,16 +45,22 @@ suite('Workbench - TerminalEnvironment', () => { test('auto', () => { assert.equal(shouldSetLangEnvVariable({}, 'auto'), true); assert.equal(shouldSetLangEnvVariable({ LANG: 'en-US' }, 'auto'), true); + assert.equal(shouldSetLangEnvVariable({ LANG: 'en-US.utf' }, 'auto'), true); + assert.equal(shouldSetLangEnvVariable({ LANG: 'en-US.utf8' }, 'auto'), false); assert.equal(shouldSetLangEnvVariable({ LANG: 'en-US.UTF-8' }, 'auto'), false); }); test('off', () => { assert.equal(shouldSetLangEnvVariable({}, 'off'), false); assert.equal(shouldSetLangEnvVariable({ LANG: 'en-US' }, 'off'), false); + assert.equal(shouldSetLangEnvVariable({ LANG: 'en-US.utf' }, 'off'), false); + assert.equal(shouldSetLangEnvVariable({ LANG: 'en-US.utf8' }, 'off'), false); assert.equal(shouldSetLangEnvVariable({ LANG: 'en-US.UTF-8' }, 'off'), false); }); test('on', () => { assert.equal(shouldSetLangEnvVariable({}, 'on'), true); assert.equal(shouldSetLangEnvVariable({ LANG: 'en-US' }, 'on'), true); + assert.equal(shouldSetLangEnvVariable({ LANG: 'en-US.utf' }, 'on'), true); + assert.equal(shouldSetLangEnvVariable({ LANG: 'en-US.utf8' }, 'on'), true); assert.equal(shouldSetLangEnvVariable({ LANG: 'en-US.UTF-8' }, 'on'), true); }); }); From 8c5bdcdfb41060a847e40c5e4a580d476fef3fb2 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 24 Aug 2020 16:13:40 +0200 Subject: [PATCH 465/736] web: API to prevent initial theme flickering (also fixes #101226) --- .../electron-browser/workbench/workbench.js | 7 +------ src/vs/platform/windows/common/windows.ts | 2 -- .../environment/browser/environmentService.ts | 5 ----- .../themes/browser/workbenchThemeService.ts | 12 +++++++++--- .../services/themes/common/colorThemeData.ts | 11 ++++++++--- .../themes/common/themeConfiguration.ts | 19 ++++--------------- src/vs/workbench/workbench.web.api.ts | 18 ++++++++++++++++++ 7 files changed, 40 insertions(+), 34 deletions(-) diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index 6642ac864eb..46340a94e38 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -79,7 +79,6 @@ bootstrapWindow.load([ * @param {{ * partsSplashPath?: string, * highContrast?: boolean, - * defaultThemeType?: string, * extensionDevelopmentPath?: string[], * folderUri?: object, * workspace?: object @@ -113,14 +112,10 @@ function showPartsSplash(configuration) { baseTheme = data.baseTheme; shellBackground = data.colorInfo.editorBackground; shellForeground = data.colorInfo.foreground; - } else if (configuration.highContrast || configuration.defaultThemeType === 'hc') { + } else if (configuration.highContrast) { baseTheme = 'hc-black'; shellBackground = '#000000'; shellForeground = '#FFFFFF'; - } else if (configuration.defaultThemeType === 'vs') { - baseTheme = 'vs'; - shellBackground = '#FFFFFF'; - shellForeground = '#000000'; } else { baseTheme = 'vs-dark'; shellBackground = '#1E1E1E'; diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 4933ec468bb..c6bd8a1c5d9 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -7,7 +7,6 @@ import { isMacintosh, isLinux, isWeb } from 'vs/base/common/platform'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ThemeType } from 'vs/platform/theme/common/themeService'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; export interface IBaseOpenWindowsOptions { @@ -182,7 +181,6 @@ export interface IWindowConfiguration { remoteAuthority?: string; highContrast?: boolean; - defaultThemeType?: ThemeType; filesToOpenOrCreate?: IPath[]; filesToDiff?: IPath[]; diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index ba2701ec54d..819607be0c1 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -14,7 +14,6 @@ import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; import product from 'vs/platform/product/common/product'; import { memoize } from 'vs/base/common/decorators'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { LIGHT } from 'vs/platform/theme/common/themeService'; import { parseLineAndColumnAware } from 'vs/base/common/extpath'; export class BrowserEnvironmentConfiguration implements IEnvironmentConfiguration { @@ -78,10 +77,6 @@ export class BrowserEnvironmentConfiguration implements IEnvironmentConfiguratio get highContrast() { return false; // could investigate to detect high contrast theme automatically } - - get defaultThemeType() { - return LIGHT; - } } interface IBrowserWorkbenchEnvironmentConstructionOptions extends IWorkbenchConstructionOptions { diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index e537e332668..3e5529f99c5 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -33,6 +33,7 @@ import { updateColorThemeConfigurationSchemas, updateFileIconThemeConfigurationS import { ProductIconThemeData, DEFAULT_PRODUCT_ICON_THEME_ID } from 'vs/workbench/services/themes/browser/productIconThemeData'; import { registerProductIconThemeSchemas } from 'vs/workbench/services/themes/common/productIconThemeSchema'; import { ILogService } from 'vs/platform/log/common/log'; +import { isWeb } from 'vs/base/common/platform'; // implementation @@ -102,8 +103,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { @ILogService private readonly logService: ILogService ) { this.container = layoutService.container; - const defaultThemeType = environmentService.configuration.defaultThemeType || DARK; - this.settings = new ThemeConfiguration(configurationService, defaultThemeType); + this.settings = new ThemeConfiguration(configurationService); this.colorThemeRegistry = new ThemeRegistry(extensionService, colorThemesExtPoint, ColorThemeData.fromExtensionTheme); this.colorThemeWatcher = new ThemeFileWatcher(fileService, environmentService, this.reloadCurrentColorTheme.bind(this)); @@ -128,7 +128,13 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { themeData = ColorThemeData.createUnloadedThemeForThemeType(HIGH_CONTRAST); } if (!themeData) { - themeData = ColorThemeData.createUnloadedThemeForThemeType(defaultThemeType); + const initialColorTheme = environmentService.options?.initialColorTheme; + if (initialColorTheme) { + themeData = ColorThemeData.createUnloadedThemeForThemeType(initialColorTheme.themeType, initialColorTheme.colors); + } + } + if (!themeData) { + themeData = ColorThemeData.createUnloadedThemeForThemeType(isWeb ? LIGHT : DARK); } themeData.setCustomizations(this.settings); this.applyTheme(themeData, undefined, true); diff --git a/src/vs/workbench/services/themes/common/colorThemeData.ts b/src/vs/workbench/services/themes/common/colorThemeData.ts index 50bbe176e9e..b8c41191476 100644 --- a/src/vs/workbench/services/themes/common/colorThemeData.ts +++ b/src/vs/workbench/services/themes/common/colorThemeData.ts @@ -550,15 +550,20 @@ export class ColorThemeData implements IWorkbenchColorTheme { // constructors - static createUnloadedThemeForThemeType(themeType: ThemeType): ColorThemeData { - return ColorThemeData.createUnloadedTheme(getThemeTypeSelector(themeType)); + static createUnloadedThemeForThemeType(themeType: ThemeType, colorMap?: { [id: string]: string }): ColorThemeData { + return ColorThemeData.createUnloadedTheme(getThemeTypeSelector(themeType), colorMap); } - static createUnloadedTheme(id: string): ColorThemeData { + static createUnloadedTheme(id: string, colorMap?: { [id: string]: string }): ColorThemeData { let themeData = new ColorThemeData(id, '', '__' + id); themeData.isLoaded = false; themeData.themeTokenColors = []; themeData.watch = false; + if (colorMap) { + for (let id in colorMap) { + themeData.colorMap[id] = Color.fromHex(colorMap[id]); + } + } return themeData; } diff --git a/src/vs/workbench/services/themes/common/themeConfiguration.ts b/src/vs/workbench/services/themes/common/themeConfiguration.ts index 4bacbe49d3d..f8276cdc5cc 100644 --- a/src/vs/workbench/services/themes/common/themeConfiguration.ts +++ b/src/vs/workbench/services/themes/common/themeConfiguration.ts @@ -14,7 +14,7 @@ import { workbenchColorsSchemaId } from 'vs/platform/theme/common/colorRegistry' import { tokenStylingSchemaId } from 'vs/platform/theme/common/tokenClassificationRegistry'; import { ThemeSettings, IWorkbenchColorTheme, IWorkbenchFileIconTheme, IColorCustomizations, ITokenColorCustomizations, IWorkbenchProductIconTheme, ISemanticTokenColorCustomizations, IExperimentalSemanticTokenColorCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { ThemeType, HIGH_CONTRAST, LIGHT } from 'vs/platform/theme/common/themeService'; +import { isWeb } from 'vs/base/common/platform'; const DEFAULT_THEME_DARK_SETTING_VALUE = 'Default Dark+'; const DEFAULT_THEME_LIGHT_SETTING_VALUE = 'Default Light+'; @@ -33,7 +33,7 @@ const colorThemeSettingEnumDescriptions: string[] = []; const colorThemeSettingSchema: IConfigurationPropertySchema = { type: 'string', description: nls.localize('colorTheme', "Specifies the color theme used in the workbench."), - default: DEFAULT_THEME_DARK_SETTING_VALUE, + default: isWeb ? DEFAULT_THEME_LIGHT_SETTING_VALUE : DEFAULT_THEME_DARK_SETTING_VALUE, enum: colorThemeSettingEnum, enumDescriptions: colorThemeSettingEnumDescriptions, errorMessage: nls.localize('colorThemeError', "Theme is unknown or not installed."), @@ -110,6 +110,7 @@ const themeSettingsConfiguration: IConfigurationNode = { [ThemeSettings.PRODUCT_ICON_THEME]: productIconThemeSettingSchema } }; +configurationRegistry.registerConfiguration(themeSettingsConfiguration); function tokenGroupSettings(description: string): IJSONSchema { return { @@ -231,19 +232,7 @@ export function updateProductIconThemeConfigurationSchemas(themes: IWorkbenchPro export class ThemeConfiguration { - constructor(private configurationService: IConfigurationService, themeType: ThemeType) { - switch (themeType) { - case LIGHT: - colorThemeSettingSchema.default = DEFAULT_THEME_LIGHT_SETTING_VALUE; - break; - case HIGH_CONTRAST: - colorThemeSettingSchema.default = DEFAULT_THEME_HC_SETTING_VALUE; - break; - default: - colorThemeSettingSchema.default = DEFAULT_THEME_DARK_SETTING_VALUE; - break; - } - configurationRegistry.registerConfiguration(themeSettingsConfiguration); + constructor(private configurationService: IConfigurationService) { } public get colorTheme(): string { diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 53612d32cdf..4386dfc2857 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -147,6 +147,15 @@ interface IWindowIndicator { command?: string; } +interface IInitialColorTheme { + themeType: 'light' | 'dark' | 'hc'; + + /** + * a list of workbench colors + */ + colors?: { [colorId: string]: string }; +} + interface IDefaultSideBarLayout { visible?: boolean; containers?: ({ @@ -381,6 +390,15 @@ interface IWorkbenchConstructionOptions { */ readonly windowIndicator?: IWindowIndicator; + /** + * Specifies the default theme type (LIGHT, DARK..) and allows to provide initial colors that are shown + * until the color theme that is specified in the settings (`editor.colorTheme`) is loaded and applied. + * Once there are persisted colors from a last run these will be used. + * + * The idea is that the colors match the main colors from the theme defined in the `configurationDefaults`. + */ + readonly initialColorTheme?: IInitialColorTheme; + //#endregion From 2951e1f3d1a5b48e35d759d87a86bfac6760c201 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 24 Aug 2020 16:34:44 +0200 Subject: [PATCH 466/736] add NotebookEditorCellEdit#replaceMetadata, https://github.com/microsoft/vscode/issues/105283 --- src/vs/vscode.proposed.d.ts | 2 +- .../workbench/api/common/extHostNotebook.ts | 9 +++ .../common/model/notebookTextModel.ts | 64 ++++++------------- .../contrib/notebook/common/notebookCommon.ts | 13 +++- 4 files changed, 39 insertions(+), 49 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index d1b72cac46f..058cebad5b7 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1356,8 +1356,8 @@ declare module 'vscode' { export interface NotebookEditorCellEdit { replaceCells(from: number, to: number, cells: NotebookCellData[]): void; - replaceOutputs(index: number, outputs: CellOutput[]): void; + replaceMetadata(index: number, metadata: NotebookCellMetadata): void; /** @deprecated */ insert(index: number, content: string | string[], language: string, type: CellKind, outputs: CellOutput[], metadata: NotebookCellMetadata | undefined): void; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index a2a995327a2..d7b304c7eee 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -511,6 +511,15 @@ export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellE } } + replaceMetadata(index: number, metadata: vscode.NotebookCellMetadata): void { + this._throwIfFinalized(); + this._collectedEdits.push({ + editType: CellEditType.Metadata, + index, + metadata + }); + } + replaceOutputs(index: number, outputs: vscode.CellOutput[]): void { this._throwIfFinalized(); this._collectedEdits.push({ diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 272b1ba7c29..905dae17035 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -8,20 +8,12 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { INotebookTextModel, NotebookCellOutputsSplice, NotebookCellTextModelSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, ICellInsertEdit, NotebookCellsChangedEvent, CellKind, IProcessedOutput, notebookDocumentMetadataDefaults, diff, ICellDeleteEdit, NotebookCellsChangeType, ICellDto2, IMainCellDto, ICellOutputEdit } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookTextModel, NotebookCellOutputsSplice, NotebookCellTextModelSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, NotebookCellsChangedEvent, CellKind, IProcessedOutput, notebookDocumentMetadataDefaults, diff, NotebookCellsChangeType, ICellDto2, IMainCellDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ITextSnapshot } from 'vs/editor/common/model'; import { IUndoRedoService, UndoRedoElementType, IUndoRedoElement, IResourceUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo'; import { InsertCellEdit, DeleteCellEdit, MoveCellEdit, SpliceCellsEdit } from 'vs/workbench/contrib/notebook/common/model/cellEdit'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -function compareRangesUsingEnds(a: [number, number], b: [number, number]): number { - if (a[1] === b[1]) { - return a[1] - b[1]; - - } - return a[1] - b[1]; -} - export class NotebookTextModelSnapshot implements ITextSnapshot { // private readonly _pieces: Ce[] = []; private _index: number = -1; @@ -235,54 +227,36 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel const oldViewCells = this.cells.slice(0); const oldMap = new Map(this._mapping); - let operations: ({ sortIndex: number; start: number; end: number; } & ICellEditOperation)[] = []; - for (let i = 0; i < rawEdits.length; i++) { - if (rawEdits[i].editType === CellEditType.Insert) { - const edit = rawEdits[i] as ICellInsertEdit; - operations.push({ - sortIndex: i, - start: edit.index, - end: edit.index, - ...edit - }); - } else { - const edit = rawEdits[i] as ICellDeleteEdit; - operations.push({ - sortIndex: i, - start: edit.index, - end: edit.index + edit.count, - ...edit - }); - } - } - - // const edits - operations = operations.sort((a, b) => { - const r = compareRangesUsingEnds([a.start, a.end], [b.start, b.end]); - if (r === 0) { - return b.sortIndex - a.sortIndex; - } - return -r; + const edits = rawEdits.map((edit, index) => { + return { + edit, + end: edit.editType === CellEditType.Delete ? edit.index + edit.count : edit.index, + originalIndex: index, + }; + }).sort((a, b) => { + return b.end - a.end || b.originalIndex - a.originalIndex; }); - for (let i = 0; i < operations.length; i++) { - switch (operations[i].editType) { + for (const { edit } of edits) { + switch (edit.editType) { case CellEditType.Insert: - const insertEdit = operations[i] as ICellInsertEdit; - const mainCells = insertEdit.cells.map(cell => { + const mainCells = edit.cells.map(cell => { const cellHandle = this._cellhandlePool++; const cellUri = CellUri.generate(this.uri, cellHandle); return new NotebookCellTextModel(cellUri, cellHandle, cell.source, cell.language, cell.cellKind, cell.outputs || [], cell.metadata, this._modelService); }); - this.insertNewCell(insertEdit.index, mainCells, false); + this.insertNewCell(edit.index, mainCells, false); break; case CellEditType.Delete: - this.removeCell(operations[i].index, operations[i].end - operations[i].start, false); + this.removeCell(edit.index, edit.count, false); break; case CellEditType.Output: //TODO@joh,@rebornix no event, no undo stop (?) - const cell = this.cells[operations[i].index]; - this.spliceNotebookCellOutputs(cell.handle, [[0, cell.outputs.length, (operations[i]).outputs]]); + const cell = this.cells[edit.index]; + this.spliceNotebookCellOutputs(cell.handle, [[0, cell.outputs.length, edit.outputs]]); + break; + case CellEditType.Metadata: + this.changeCellMetadata(this.cells[edit.index].handle, edit.metadata); break; } } diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index ff722330fc1..98d437930ee 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -403,6 +403,7 @@ export const enum CellEditType { Insert = 1, Delete = 2, Output = 3, + Metadata = 4, } export interface ICellDto2 { @@ -427,11 +428,17 @@ export interface ICellDeleteEdit { export interface ICellOutputEdit { editType: CellEditType.Output; - index: number, - outputs: IProcessedOutput[] + index: number; + outputs: IProcessedOutput[]; } -export type ICellEditOperation = ICellInsertEdit | ICellDeleteEdit | ICellOutputEdit; +export interface ICellMetadataEdit { + editType: CellEditType.Metadata; + index: number; + metadata: NotebookCellMetadata; +} + +export type ICellEditOperation = ICellInsertEdit | ICellDeleteEdit | ICellOutputEdit | ICellMetadataEdit; export interface INotebookEditData { documentVersionId: number; From a58fa935d7253a81a32e85ba38bcc46499e87b5b Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 24 Aug 2020 16:41:15 +0200 Subject: [PATCH 467/736] debug: do not register things lazily fixes #105278 --- .../contrib/debug/browser/debug.contribution.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 5e9db371cae..9ef3c011490 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -51,23 +51,19 @@ import { DebugProgressContribution } from 'vs/workbench/contrib/debug/browser/de import { DebugTitleContribution } from 'vs/workbench/contrib/debug/browser/debugTitle'; import { Codicon } from 'vs/base/common/codicons'; import { registerColors } from 'vs/workbench/contrib/debug/browser/debugColors'; -import { debugAdapterRegisteredEmitter } from 'vs/workbench/contrib/debug/browser/debugConfigurationManager'; import { DebugEditorContribution } from 'vs/workbench/contrib/debug/browser/debugEditorContribution'; const registry = Registry.as(WorkbenchActionRegistryExtensions.WorkbenchActions); +const debugCategory = nls.localize('debugCategory', "Debug"); +const runCategroy = nls.localize('runCategory', "Run"); // register service -debugAdapterRegisteredEmitter.event(() => { - // Register these contributions lazily only once a debug adapter extension has been registered - registerWorkbenchContributions(); - registerColors(); - registerCommandsAndActions(); - registerDebugMenu(); -}); +registerWorkbenchContributions(); +registerColors(); +registerCommandsAndActions(); +registerDebugMenu(); registerEditorActions(); registerCommands(); registerDebugPanel(); -const debugCategory = nls.localize('debugCategory', "Debug"); -const runCategroy = nls.localize('runCategory', "Run"); registry.registerWorkbenchAction(SyncActionDescriptor.from(StartAction, { primary: KeyCode.F5 }, CONTEXT_IN_DEBUG_MODE.toNegated()), 'Debug: Start Debugging', debugCategory); registry.registerWorkbenchAction(SyncActionDescriptor.from(RunAction, { primary: KeyMod.CtrlCmd | KeyCode.F5, mac: { primary: KeyMod.WinCtrl | KeyCode.F5 } }), 'Run: Start Without Debugging', runCategroy); From 88664e267a5ec3d7ed43a542b4cd3c8e456a9fd9 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 24 Aug 2020 16:58:29 +0200 Subject: [PATCH 468/736] npm: avoid invalid lookups --- .../src/features/packageJSONContribution.ts | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/extensions/npm/src/features/packageJSONContribution.ts b/extensions/npm/src/features/packageJSONContribution.ts index 0e24b45aa28..f154a875239 100644 --- a/extensions/npm/src/features/packageJSONContribution.ts +++ b/extensions/npm/src/features/packageJSONContribution.ts @@ -249,7 +249,27 @@ export class PackageJSONContribution implements IJSONContribution { return null; } + private isValidNPMName(name: string): boolean { + // following rules from https://github.com/npm/validate-npm-package-name + if (!name || name.length > 214 || name.match(/^[_.]/)) { + return false; + } + const match = name.match(/^(?:@([^/]+?)[/])?([^/]+?)$/); + if (match) { + const scope = match[1]; + if (scope && encodeURIComponent(scope) !== scope) { + return false; + } + const name = match[2]; + return encodeURIComponent(name) === name; + } + return true; + } + private async fetchPackageInfo(pack: string): Promise { + if (!this.isValidNPMName(pack)) { + return undefined; // avoid unnecessary lookups + } let info: ViewPackageInfo | undefined; if (this.canRunNPM) { info = await this.npmView(pack); @@ -260,7 +280,6 @@ export class PackageJSONContribution implements IJSONContribution { return info; } - private npmView(pack: string): Promise { return new Promise((resolve, _reject) => { const command = 'npm view --json ' + pack + ' description dist-tags.latest homepage version'; From cf5dfe1bc846c56a98116f581fdf0cc10d7383cd Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 24 Aug 2020 17:11:45 +0200 Subject: [PATCH 469/736] unit test for applying metadata and output, https://github.com/microsoft/vscode/issues/105283 --- .../common/model/notebookTextModel.ts | 2 + .../notebook/test/notebookTextModel.test.ts | 87 ++++++++++++++++++- .../notebook/test/testNotebookEditor.ts | 12 ++- 3 files changed, 97 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 905dae17035..fbe20ae1e08 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -252,10 +252,12 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel break; case CellEditType.Output: //TODO@joh,@rebornix no event, no undo stop (?) + this.assertIndex(edit.index); const cell = this.cells[edit.index]; this.spliceNotebookCellOutputs(cell.handle, [[0, cell.outputs.length, edit.outputs]]); break; case CellEditType.Metadata: + this.assertIndex(edit.index); this.changeCellMetadata(this.cells[edit.index].handle, edit.metadata); break; } diff --git a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts index b0e9bf0424e..444121d2af7 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { CellKind, CellEditType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellEditType, CellOutputKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { withTestNotebook, TestCell, setupInstantiationService } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; @@ -139,4 +139,89 @@ suite('NotebookTextModel', () => { } ); }); + + test('output', function () { + withTestNotebook( + instantiationService, + blukEditService, + undoRedoService, + [ + ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], + ], + (editor, viewModel, textModel) => { + + // invalid index 1 + assert.throws(() => { + textModel.applyEdit(textModel.versionId, [{ + index: Number.MAX_VALUE, + editType: CellEditType.Output, + outputs: [] + }], true); + }); + + // invalid index 2 + assert.throws(() => { + textModel.applyEdit(textModel.versionId, [{ + index: -1, + editType: CellEditType.Output, + outputs: [] + }], true); + }); + + textModel.applyEdit(textModel.versionId, [{ + index: 0, + editType: CellEditType.Output, + outputs: [{ + outputKind: CellOutputKind.Rich, + outputId: 'someId', + data: { 'text/markdown': '_Hello_' } + }] + }], true); + + assert.equal(textModel.cells.length, 1); + assert.equal(textModel.cells[0].outputs.length, 1); + assert.equal(textModel.cells[0].outputs[0].outputKind, CellOutputKind.Rich); + } + ); + }); + + test('metadata', function () { + withTestNotebook( + instantiationService, + blukEditService, + undoRedoService, + [ + ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], + ], + (editor, viewModel, textModel) => { + + // invalid index 1 + assert.throws(() => { + textModel.applyEdit(textModel.versionId, [{ + index: Number.MAX_VALUE, + editType: CellEditType.Metadata, + metadata: { editable: false } + }], true); + }); + + // invalid index 2 + assert.throws(() => { + textModel.applyEdit(textModel.versionId, [{ + index: -1, + editType: CellEditType.Metadata, + metadata: { editable: false } + }], true); + }); + + textModel.applyEdit(textModel.versionId, [{ + index: 0, + editType: CellEditType.Metadata, + metadata: { editable: false }, + }], true); + + assert.equal(textModel.cells.length, 1); + assert.equal(textModel.cells[0].metadata?.editable, false); + } + ); + }); }); diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index eebd1a26cf0..2c4f00ac784 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -381,9 +381,15 @@ export function withTestNotebook(instantiationService: TestInstantiationService, const viewType = 'notebook'; const editor = new TestNotebookEditor(); const notebook = new NotebookTextModel(0, viewType, false, URI.parse('test'), undoRedoService, textModelService); - notebook.cells = cells.map((cell, index) => { - return new NotebookCellTextModel(notebook.uri, index, cell[0], cell[1], cell[2], cell[3], cell[4], textModelService); - }); + notebook.initialize(cells.map(cell => { + return { + source: cell[0], + language: cell[1], + cellKind: cell[2], + outputs: cell[3], + metadata: cell[4] + }; + })); const model = new NotebookEditorTestModel(notebook); const eventDispatcher = new NotebookEventDispatcher(); const viewModel = new NotebookViewModel(viewType, model.notebook, eventDispatcher, null, instantiationService, blukEditService, undoRedoService); From 32dde5598df15c19d76aa197dad05a16382b49c7 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 24 Aug 2020 08:43:11 -0700 Subject: [PATCH 470/736] Use addWebviewInput Fixes #105295 --- src/vs/workbench/api/browser/mainThreadWebview.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index 5fedd8e6935..ec9c8e18ef5 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -175,10 +175,9 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma } const extension = reviveWebviewExtension(extensionData); - const webview = this._webviewWorkbenchService.createWebview(handle, this.webviewPanelViewType.fromExternal(viewType), title, mainThreadShowOptions, reviveWebviewOptions(options), extension); - this.hookupWebviewEventDelegate(handle, webview.webview); - this._webviewInputs.add(handle, webview); + const webview = this._webviewWorkbenchService.createWebview(handle, this.webviewPanelViewType.fromExternal(viewType), title, mainThreadShowOptions, reviveWebviewOptions(options), extension); + this.addWebviewInput(handle, webview); /* __GDPR__ "webviews:createWebviewPanel" : { From 86593ed18ec7dcb81d7cd87f503261043b275642 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 24 Aug 2020 10:19:44 -0700 Subject: [PATCH 471/736] flex layout and shared metadata component --- .../notebook/browser/diff/cellComponents.ts | 96 +++++++++++-------- .../notebook/browser/diff/notebookDiff.css | 63 ++++++++---- .../browser/diff/notebookTextDiffEditor.ts | 2 +- 3 files changed, 97 insertions(+), 64 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index c2ac2ebc30b..79fae866e45 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -79,11 +79,14 @@ enum MetadataFoldingState { abstract class AbstractCellRenderer extends Disposable { protected _metadataHeaderContainer!: HTMLElement; protected _metadataInfoContainer!: HTMLElement; + protected _diffEditorContainer!: HTMLElement; + protected _diagonalFill?: HTMLElement; protected _layoutInfo!: { editorHeight: number; editorMargin: number; metadataStatusHeight: number; metadataHeight: number; + bodyMargin: number; }; protected _foldingIndicator!: HTMLElement; protected _foldingState!: MetadataFoldingState; @@ -95,6 +98,7 @@ abstract class AbstractCellRenderer extends Disposable { readonly notebookEditor: INotebookTextDiffEditor, readonly cell: CellDiffViewModel, readonly templateData: CellDiffRenderTemplate, + readonly style: 'left' | 'right' | 'full', protected readonly instantiationService: IInstantiationService, protected readonly modeService: IModeService, protected readonly modelService: IModelService, @@ -104,9 +108,10 @@ abstract class AbstractCellRenderer extends Disposable { // init this._layoutInfo = { editorHeight: 0, - editorMargin: 32, + editorMargin: 0, metadataHeight: 0, - metadataStatusHeight: 24 + metadataStatusHeight: 25, + bodyMargin: 16 }; this._metadataEditorDisposeStore = new DisposableStore(); this._foldingState = MetadataFoldingState.Collapsed; @@ -116,16 +121,32 @@ abstract class AbstractCellRenderer extends Disposable { } buildBody(container: HTMLElement) { - const diffEditorContainer = DOM.$('.cell-diff-editor-container'); - DOM.append(container, diffEditorContainer); - this.styleContainer(diffEditorContainer); - const sourceContainer = DOM.append(diffEditorContainer, DOM.$('.source-container')); + const body = DOM.$('.cell-body'); + DOM.append(container, body); + this._diffEditorContainer = DOM.$('.cell-diff-editor-container'); + switch (this.style) { + case 'left': + DOM.addClass(body, 'left'); + break; + case 'right': + DOM.addClass(body, 'right'); + break; + default: + DOM.addClass(body, 'full'); + break; + } + + DOM.append(body, this._diffEditorContainer); + this._diagonalFill = DOM.append(body, DOM.$('.diagonal-fill')); + // this._diagonalFill.style.display = '0px'; + this.styleContainer(this._diffEditorContainer); + const sourceContainer = DOM.append(this._diffEditorContainer, DOM.$('.source-container')); this.buildSourceEditor(sourceContainer); - this._metadataHeaderContainer = DOM.append(diffEditorContainer, DOM.$('.metadata-header-container')); - this._metadataInfoContainer = DOM.append(diffEditorContainer, DOM.$('.metadata-info-container')); + this._metadataHeaderContainer = DOM.append(this._diffEditorContainer, DOM.$('.metadata-header-container')); + this._metadataInfoContainer = DOM.append(this._diffEditorContainer, DOM.$('.metadata-info-container')); this.buildMetadataHeader(this._metadataHeaderContainer); - this.buildMetadataBody(this._metadataInfoContainer); + // this.buildMetadataBody(this._metadataInfoContainer); } buildMetadataHeader(metadataHeaderContainer: HTMLElement): void { @@ -225,8 +246,6 @@ abstract class AbstractCellRenderer extends Disposable { abstract initData(): void; abstract styleContainer(container: HTMLElement): void; abstract buildSourceEditor(sourceContainer: HTMLElement): void; - abstract buildMetadataBody(metadataBodyContainer: HTMLElement): void; - abstract onDidLayoutChange(event: CellDiffViewModelLayoutChangeEvent): void; abstract layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean }): void; } @@ -242,7 +261,7 @@ export class UnchangedCell extends AbstractCellRenderer { @IModelService protected modelService: IModelService, @IInstantiationService protected readonly instantiationService: IInstantiationService, ) { - super(notebookEditor, cell, templateData, instantiationService, modeService, modelService); + super(notebookEditor, cell, templateData, 'full', instantiationService, modeService, modelService); } initData() { @@ -284,9 +303,6 @@ export class UnchangedCell extends AbstractCellRenderer { }); } - buildMetadataBody(metadataBodyContainer: HTMLElement): void { - - } onDidLayoutChange(event: CellDiffViewModelLayoutChangeEvent): void { if (event.outerWidth !== undefined) { @@ -310,13 +326,12 @@ export class UnchangedCell extends AbstractCellRenderer { } this.notebookEditor.layoutNotebookCell(this.cell, - this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight); + this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight + this._layoutInfo.bodyMargin); } } export class DeletedCell extends AbstractCellRenderer { private _editor!: CodeEditorWidget; - private _diagonalFill!: HTMLElement; constructor( readonly notebookEditor: INotebookTextDiffEditor, readonly cell: CellDiffViewModel, @@ -325,14 +340,13 @@ export class DeletedCell extends AbstractCellRenderer { @IModelService readonly modelService: IModelService, @IInstantiationService protected readonly instantiationService: IInstantiationService, ) { - super(notebookEditor, cell, templateData, instantiationService, modeService, modelService); + super(notebookEditor, cell, templateData, 'left', instantiationService, modeService, modelService); } initData(): void { } styleContainer(container: HTMLElement) { - DOM.addClass(container, 'delete'); } buildSourceEditor(sourceContainer: HTMLElement): void { @@ -342,7 +356,6 @@ export class DeletedCell extends AbstractCellRenderer { const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; const editorContainer = DOM.append(sourceContainer, DOM.$('.editor-container')); - this._diagonalFill = DOM.append(sourceContainer, DOM.$('.diagonal-fill')); this._editor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, { ...fixedEditorOptions, @@ -352,7 +365,10 @@ export class DeletedCell extends AbstractCellRenderer { } }, {}); - this._diagonalFill.style.height = `${editorHeight}px`; + if (this._diagonalFill) { + this._layoutInfo.editorHeight = editorHeight; + // this._diagonalFill.style.height = `${this._diffEditorContainer.clientHeight}px`; + } this._register(this._editor.onDidContentSizeChange((e) => { if (e.contentHeightChanged) { @@ -372,9 +388,6 @@ export class DeletedCell extends AbstractCellRenderer { } - buildMetadataBody(metadataBodyContainer: HTMLElement): void { - } - onDidLayoutChange(e: CellDiffViewModelLayoutChangeEvent) { if (e.outerWidth !== undefined) { this.layout({ outerWidth: true }); @@ -390,21 +403,22 @@ export class DeletedCell extends AbstractCellRenderer { if (state.metadataEditor || state.outerWidth) { this._metadataEditor?.layout({ - width: this.notebookEditor.getLayoutInfo().width - 20, + width: (this.notebookEditor.getLayoutInfo().width - 20) / 2 - 18, height: this._layoutInfo.metadataHeight }); } - this._diagonalFill.style.height = `${this._layoutInfo.editorHeight}px`; + if (this._diagonalFill) { + // this._diagonalFill.style.height = `${this._diffEditorContainer.clientHeight}px`; + } this.notebookEditor.layoutNotebookCell(this.cell, - this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight); + this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight + this._layoutInfo.bodyMargin); } } export class InsertCell extends AbstractCellRenderer { private _editor!: CodeEditorWidget; - private _diagonalFill!: HTMLElement; constructor( readonly notebookEditor: INotebookTextDiffEditor, readonly cell: CellDiffViewModel, @@ -413,14 +427,13 @@ export class InsertCell extends AbstractCellRenderer { @IModeService readonly modeService: IModeService, @IModelService readonly modelService: IModelService, ) { - super(notebookEditor, cell, templateData, instantiationService, modeService, modelService); + super(notebookEditor, cell, templateData, 'right', instantiationService, modeService, modelService); } initData(): void { } styleContainer(container: HTMLElement): void { - DOM.addClass(container, 'insert'); } buildSourceEditor(sourceContainer: HTMLElement): void { @@ -428,7 +441,6 @@ export class InsertCell extends AbstractCellRenderer { const lineCount = modifiedCell.textBuffer.getLineCount(); const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; - this._diagonalFill = DOM.append(sourceContainer, DOM.$('.diagonal-fill')); const editorContainer = DOM.append(sourceContainer, DOM.$('.editor-container')); this._editor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, { @@ -439,7 +451,11 @@ export class InsertCell extends AbstractCellRenderer { } }, {}); - this._diagonalFill.style.height = `${editorHeight}px`; + this._layoutInfo.editorHeight = editorHeight; + + if (this._diagonalFill) { + this._diagonalFill.style.height = `${this._diffEditorContainer.clientHeight}px`; + } this._register(this._editor.onDidContentSizeChange((e) => { if (e.contentHeightChanged) { @@ -458,9 +474,6 @@ export class InsertCell extends AbstractCellRenderer { }); } - buildMetadataBody(metadataBodyContainer: HTMLElement): void { - } - onDidLayoutChange(e: CellDiffViewModelLayoutChangeEvent) { if (e.outerWidth !== undefined) { this.layout({ outerWidth: true }); @@ -482,10 +495,12 @@ export class InsertCell extends AbstractCellRenderer { }); } - this._diagonalFill.style.height = `${this._layoutInfo.editorHeight}px`; + if (this._diagonalFill) { + this._diagonalFill.style.height = `${this._diffEditorContainer.clientHeight}px`; + } this.notebookEditor.layoutNotebookCell(this.cell, - this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight); + this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight + this._layoutInfo.bodyMargin); } } @@ -500,7 +515,7 @@ export class ModifiedCell extends AbstractCellRenderer { @IModeService readonly modeService: IModeService, @IModelService readonly modelService: IModelService, ) { - super(notebookEditor, cell, templateData, instantiationService, modeService, modelService); + super(notebookEditor, cell, templateData, 'full', instantiationService, modeService, modelService); } initData(): void { @@ -559,9 +574,6 @@ export class ModifiedCell extends AbstractCellRenderer { } - buildMetadataBody(metadataBodyContainer: HTMLElement): void { - } - onDidLayoutChange(e: CellDiffViewModelLayoutChangeEvent) { if (e.outerWidth !== undefined) { this.layout({ outerWidth: true }); @@ -582,7 +594,7 @@ export class ModifiedCell extends AbstractCellRenderer { } this.notebookEditor.layoutNotebookCell(this.cell, - this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight); + this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight + this._layoutInfo.bodyMargin); } } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css index 7b023ccc64c..ff4a478dfd4 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css @@ -16,8 +16,49 @@ width: 50%; } */ -.notebook-text-diff-editor .cell-diff-editor-container { +/* .notebook-text-diff-editor { margin: 8px; +} */ + +.notebook-text-diff-editor .cell-body { + display: flex; + flex-direction: row; + margin: 8px; +} + +.notebook-text-diff-editor .cell-body .diagonal-fill { + display: none; + width: 50%; +} + +.notebook-text-diff-editor .cell-body .cell-diff-editor-container { + width: 100%; +} + +.notebook-text-diff-editor .cell-body.left .cell-diff-editor-container, +.notebook-text-diff-editor .cell-body.right .cell-diff-editor-container { + display: inline-block; +} + +.notebook-text-diff-editor .cell-body.left .diagonal-fill, +.notebook-text-diff-editor .cell-body.right .diagonal-fill { + display: inline-block; +} + +.notebook-text-diff-editor .cell-body.left .cell-diff-editor-container { + width: calc(50% - 18px); +} + +.notebook-text-diff-editor .cell-body.left .diagonal-fill { + width: calc(50% + 18px); +} + +.notebook-text-diff-editor .cell-body.right .cell-diff-editor-container { + width: calc(50% + 18px); +} + +.notebook-text-diff-editor .cell-body.right .diagonal-fill { + width: calc(50% - 18px); } .notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container { @@ -42,26 +83,6 @@ line-height: 21px; } -.notebook-text-diff-editor .cell-diff-editor-container.delete .editor-container { - display: inline-block; - width: calc(50% - 18px); -} - -.notebook-text-diff-editor .cell-diff-editor-container.delete .diagonal-fill { - display: inline-block; - width: calc(50% + 18px); -} - -.notebook-text-diff-editor .cell-diff-editor-container.insert .editor-container { - display: inline-block; - width: calc(50% + 18px); -} - -.notebook-text-diff-editor .cell-diff-editor-container.insert .diagonal-fill { - display: inline-block; - width: calc(50% - 18px); -} - .notebook-text-diff-editor { overflow: hidden; } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index d6fb2853758..bc6413d2b4a 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -288,7 +288,7 @@ export class NotebookTextDiffEditor extends BaseEditor implements INotebookTextD registerThemingParticipant((theme, collector) => { const cellBorderColor = theme.getColor(notebookCellBorder); if (cellBorderColor) { - collector.addRule(`.notebook-text-diff-editor .editor-container { border: 1px solid ${cellBorderColor};}`); + collector.addRule(`.notebook-text-diff-editor .source-container { border: 1px solid ${cellBorderColor};}`); collector.addRule(`.notebook-text-diff-editor .metadata-editor-container { border-left: 1px solid ${cellBorderColor}; border-right: 1px solid ${cellBorderColor}; From bb624e94f2a9c01b19e9ad89303b157df87ce93b Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 24 Aug 2020 19:20:23 +0200 Subject: [PATCH 472/736] always allow show notifications command so screen reader users can focus the notifications toast --- .../browser/parts/notifications/notificationsCommands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts index e41c8692e57..a935e757aad 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts @@ -221,7 +221,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl // Commands for Command Palette const category = { value: localize('notifications', "Notifications"), original: 'Notifications' }; - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: SHOW_NOTIFICATIONS_CENTER, title: { value: localize('showNotifications', "Show Notifications"), original: 'Show Notifications' }, category }, when: NotificationsCenterVisibleContext.toNegated() }); + MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: SHOW_NOTIFICATIONS_CENTER, title: { value: localize('showNotifications', "Show Notifications"), original: 'Show Notifications' }, category } }); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: HIDE_NOTIFICATIONS_CENTER, title: { value: localize('hideNotifications', "Hide Notifications"), original: 'Hide Notifications' }, category }, when: NotificationsCenterVisibleContext }); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLEAR_ALL_NOTIFICATIONS, title: { value: localize('clearAllNotifications', "Clear All Notifications"), original: 'Clear All Notifications' }, category } }); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: FOCUS_NOTIFICATION_TOAST, title: { value: localize('focusNotificationToasts', "Focus Notification Toast"), original: 'Focus Notification Toast' }, category }, when: NotificationsToastsVisibleContext }); From 3f392ec5a379b8f4c58f83ecdcf495acd29d9aeb Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Mon, 24 Aug 2020 10:44:37 -0700 Subject: [PATCH 473/736] Revert github-authentication extensionKind --- extensions/github-authentication/package.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index 8136ebd04ca..c4189e6b7eb 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -15,11 +15,6 @@ "*", "onAuthenticationRequest:github" ], - "extensionKind": [ - "ui", - "workspace", - "web" - ], "contributes": { "commands": [ { From ec2dd67ae326646b6eeb90144c189aa8b02b2daf Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 24 Aug 2020 10:51:47 -0700 Subject: [PATCH 474/736] store folding state in cell view model. --- .../notebook/browser/diff/cellComponents.ts | 17 +++++------------ .../notebook/browser/diff/celllDiffViewModel.ts | 8 +++++++- .../contrib/notebook/browser/diff/common.ts | 3 ++- .../browser/diff/notebookTextDiffList.ts | 17 +++++++++++------ 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index 79fae866e45..5ccc0dc3f33 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -7,7 +7,7 @@ import * as DOM from 'vs/base/browser/dom'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { CellDiffViewModel } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; +import { CellDiffViewModel, MetadataFoldingState } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; import { CellDiffRenderTemplate, CellDiffViewModelLayoutChangeEvent, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/common'; import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; @@ -71,11 +71,6 @@ const fixedEditorOptions: IEditorOptions = { renderValidationDecorations: 'on' }; -enum MetadataFoldingState { - Expanded, - Collapsed -} - abstract class AbstractCellRenderer extends Disposable { protected _metadataHeaderContainer!: HTMLElement; protected _metadataInfoContainer!: HTMLElement; @@ -89,7 +84,6 @@ abstract class AbstractCellRenderer extends Disposable { bodyMargin: number; }; protected _foldingIndicator!: HTMLElement; - protected _foldingState!: MetadataFoldingState; protected _metadataEditorContainer?: HTMLElement; protected _metadataEditorDisposeStore!: DisposableStore; protected _metadataEditor?: CodeEditorWidget; @@ -114,7 +108,6 @@ abstract class AbstractCellRenderer extends Disposable { bodyMargin: 16 }; this._metadataEditorDisposeStore = new DisposableStore(); - this._foldingState = MetadataFoldingState.Collapsed; this.initData(); this.buildBody(templateData.container); this._register(cell.onDidLayoutChange(e => this.onDidLayoutChange(e))); @@ -175,7 +168,7 @@ abstract class AbstractCellRenderer extends Disposable { const cellViewModel = e.target; if (cellViewModel === this.cell) { - this._foldingState = this._foldingState === MetadataFoldingState.Expanded ? MetadataFoldingState.Collapsed : MetadataFoldingState.Expanded; + this.cell.foldingState = this.cell.foldingState === MetadataFoldingState.Expanded ? MetadataFoldingState.Collapsed : MetadataFoldingState.Expanded; this.updateMetadataRendering(); } } @@ -187,7 +180,7 @@ abstract class AbstractCellRenderer extends Disposable { } updateMetadataRendering() { - if (this._foldingState === MetadataFoldingState.Expanded) { + if (this.cell.foldingState === MetadataFoldingState.Expanded) { // we should expand the metadata editor this._metadataInfoContainer.style.display = 'block'; @@ -214,7 +207,7 @@ abstract class AbstractCellRenderer extends Disposable { this.layout({ metadataEditor: true }); this._register(this._metadataEditor.onDidContentSizeChange((e) => { - if (e.contentHeightChanged && this._foldingState === MetadataFoldingState.Expanded) { + if (e.contentHeightChanged && this.cell.foldingState === MetadataFoldingState.Expanded) { this._layoutInfo.metadataHeight = e.contentHeight; this.layout({ metadataEditor: true }); } @@ -236,7 +229,7 @@ abstract class AbstractCellRenderer extends Disposable { } private _updateFoldingIcon() { - if (this._foldingState === MetadataFoldingState.Collapsed) { + if (this.cell.foldingState === MetadataFoldingState.Collapsed) { this._foldingIndicator.innerHTML = renderCodicons('$(chevron-right)'); } else { this._foldingIndicator.innerHTML = renderCodicons('$(chevron-down)'); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel.ts b/src/vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel.ts index ce5c353ed71..602509f535b 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel.ts @@ -6,11 +6,16 @@ import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookDiffEditorEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { Emitter } from 'vs/base/common/event'; -import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { Disposable } from 'vs/base/common/lifecycle'; import { CellDiffViewModelLayoutChangeEvent } from 'vs/workbench/contrib/notebook/browser/diff/common'; +export enum MetadataFoldingState { + Expanded, + Collapsed +} + export class CellDiffViewModel extends Disposable { + public foldingState: MetadataFoldingState; private _layoutInfoEmitter = new Emitter(); onDidLayoutChange = this._layoutInfoEmitter.event; @@ -22,6 +27,7 @@ export class CellDiffViewModel extends Disposable { readonly editorEventDispatcher: NotebookDiffEditorEventDispatcher ) { super(); + this.foldingState = MetadataFoldingState.Collapsed; this._register(this.editorEventDispatcher.onDidChangeLayout(e => { this._layoutInfoEmitter.fire({ outerWidth: e.value.width }); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/common.ts b/src/vs/workbench/contrib/notebook/browser/diff/common.ts index fd6ac29ca3d..ea439226ed7 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/common.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/common.ts @@ -7,6 +7,7 @@ import { NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebo import { CellDiffViewModel } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; import { Event } from 'vs/base/common/event'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; +import { DisposableStore } from 'vs/base/common/lifecycle'; export interface INotebookTextDiffEditor { onMouseUp: Event<{ readonly event: MouseEvent; readonly target: CellDiffViewModel; }>; @@ -16,7 +17,7 @@ export interface INotebookTextDiffEditor { export interface CellDiffRenderTemplate { readonly container: HTMLElement; - + readonly elementDisposables: DisposableStore; } export interface CellDiffViewModelLayoutChangeEvent { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts index 97646bae5d2..705c261a2cc 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts @@ -7,7 +7,7 @@ import 'vs/css!./notebookDiff'; import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import * as DOM from 'vs/base/browser/dom'; import { IListStyles, IStyleController } from 'vs/base/browser/ui/list/listWidget'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -55,7 +55,8 @@ export class CellDiffRenderer implements IListRenderer Date: Tue, 25 Aug 2020 02:18:43 +0800 Subject: [PATCH 475/736] update markdown themes --- .../theme-abyss/themes/abyss-color-theme.json | 25 ++++++++++++--- .../themes/hc_black_defaults.json | 1 + .../themes/kimbie-dark-color-theme.json | 2 +- .../theme-red/themes/Red-color-theme.json | 32 +++++++++++++------ .../themes/solarized-dark-color-theme.json | 15 +++++++++ .../themes/solarized-light-color-theme.json | 15 +++++++++ .../themes/tomorrow-night-blue-theme.json | 21 ++++++++++++ 7 files changed, 96 insertions(+), 15 deletions(-) diff --git a/extensions/theme-abyss/themes/abyss-color-theme.json b/extensions/theme-abyss/themes/abyss-color-theme.json index 39f93305b8d..7afe3bd963e 100644 --- a/extensions/theme-abyss/themes/abyss-color-theme.json +++ b/extensions/theme-abyss/themes/abyss-color-theme.json @@ -233,6 +233,20 @@ "foreground": "#22aa44" } }, + { + "name": "Markup: Strong", + "scope": "markup.bold", + "settings": { + "fontStyle": "bold" + } + }, + { + "name": "Markup: Emphasis", + "scope": "markup.italic", + "settings": { + "fontStyle": "italic" + } + }, { "name": "Markup Inline", "scope": "markup.inline.raw", @@ -242,11 +256,14 @@ } }, { - "name": "Markup Setext Header", - "scope": "markup.heading.setext", + "name": "Markup Headings", + "scope": [ + "markup.heading", + "markup.heading.setext" + ], "settings": { - "fontStyle": "", - "foreground": "#ddbb88" + "fontStyle": "bold", + "foreground": "#6688cc" } } ], diff --git a/extensions/theme-defaults/themes/hc_black_defaults.json b/extensions/theme-defaults/themes/hc_black_defaults.json index 1a03010abff..495a15238dc 100644 --- a/extensions/theme-defaults/themes/hc_black_defaults.json +++ b/extensions/theme-defaults/themes/hc_black_defaults.json @@ -136,6 +136,7 @@ { "scope": "markup.heading", "settings": { + "fontStyle": "bold", "foreground": "#6796e6" } }, diff --git a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json index cdd22307117..38c8fe09968 100644 --- a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json +++ b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json @@ -260,7 +260,7 @@ "entity.name.section" ], "settings": { - "fontStyle": "", + "fontStyle": "bold", "foreground": "#8ab1b0" } }, diff --git a/extensions/theme-red/themes/Red-color-theme.json b/extensions/theme-red/themes/Red-color-theme.json index 8964f40a093..dbe80113209 100644 --- a/extensions/theme-red/themes/Red-color-theme.json +++ b/extensions/theme-red/themes/Red-color-theme.json @@ -350,6 +350,20 @@ "foreground": "#fb9a4bff" } }, + { + "name": "Markup: Strong", + "scope": "markup.bold", + "settings": { + "fontStyle": "bold" + } + }, + { + "name": "Markup: Emphasis", + "scope": "markup.italic", + "settings": { + "fontStyle": "italic" + } + }, { "name": "Markup Inline", "scope": "markup.inline.raw", @@ -359,17 +373,15 @@ } }, { - "name": "Markup Headings", - "scope": "markup.heading", + "name": "Headings", + "scope": [ + "markup.heading", + "markup.heading.setext", + "punctuation.definition.heading", + "entity.name.section" + ], "settings": { - "foreground": "#fec758ff" - } - }, - { - "name": "Markup Setext Header", - "scope": "markup.heading.setext", - "settings": { - "fontStyle": "", + "fontStyle": "bold", "foreground": "#fec758ff" } }, diff --git a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json index b23ff8bb85c..eaf90258d35 100644 --- a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json +++ b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json @@ -270,6 +270,20 @@ "foreground": "#D33682" } }, + { + "name": "Markup: Strong", + "scope": "markup.bold", + "settings": { + "fontStyle": "bold" + } + }, + { + "name": "Markup: Emphasis", + "scope": "markup.italic", + "settings": { + "fontStyle": "italic" + } + }, { "name": "Markup Inline", "scope": "markup.inline.raw", @@ -282,6 +296,7 @@ "name": "Markup Headings", "scope": "markup.heading", "settings": { + "fontStyle": "bold", "foreground": "#268BD2" } }, diff --git a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json index 21f530d00a3..77aa0f29079 100644 --- a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json +++ b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json @@ -273,6 +273,20 @@ "foreground": "#D33682" } }, + { + "name": "Markup: Strong", + "scope": "markup.bold", + "settings": { + "fontStyle": "bold" + } + }, + { + "name": "Markup: Emphasis", + "scope": "markup.italic", + "settings": { + "fontStyle": "italic" + } + }, { "name": "Markup Inline", "scope": "markup.inline.raw", @@ -285,6 +299,7 @@ "name": "Markup Headings", "scope": "markup.heading", "settings": { + "fontStyle": "bold", "foreground": "#268BD2" } }, diff --git a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json index 0baee6822ef..bdccdb49d91 100644 --- a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json +++ b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json @@ -223,6 +223,20 @@ "foreground": "#FFC58F" } }, + { + "name": "Markup: Strong", + "scope": "markup.bold", + "settings": { + "fontStyle": "bold" + } + }, + { + "name": "Markup: Emphasis", + "scope": "markup.italic", + "settings": { + "fontStyle": "italic" + } + }, { "name": "Markup Inline", "scope": "markup.inline.raw", @@ -231,6 +245,13 @@ "foreground": "#FF9DA4" } }, + { + "name": "Markup Headings", + "scope": "markup.heading", + "settings": { + "fontStyle": "bold" + } + }, { "scope": "token.info-token", "settings": { From 8fcf75aff2f05fe0bf5bdb601d5c633cfd6125e0 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 24 Aug 2020 11:48:44 -0700 Subject: [PATCH 476/736] always expand metadata editor if it is changed. --- .../notebook/browser/diff/cellComponents.ts | 112 +++++++++++++----- .../notebook/browser/diff/notebookDiff.css | 4 - 2 files changed, 84 insertions(+), 32 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index 5ccc0dc3f33..dcfb5b2b6b9 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -17,6 +17,8 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { format } from 'vs/base/common/jsonFormatter'; import { applyEdits } from 'vs/base/common/jsonEdit'; +import { NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { hash } from 'vs/base/common/hash'; const fixedDiffEditorOptions: IEditorOptions = { @@ -74,6 +76,7 @@ const fixedEditorOptions: IEditorOptions = { abstract class AbstractCellRenderer extends Disposable { protected _metadataHeaderContainer!: HTMLElement; protected _metadataInfoContainer!: HTMLElement; + protected _metadataStatusSpan!: HTMLElement; protected _diffEditorContainer!: HTMLElement; protected _diagonalFill?: HTMLElement; protected _layoutInfo!: { @@ -86,7 +89,7 @@ abstract class AbstractCellRenderer extends Disposable { protected _foldingIndicator!: HTMLElement; protected _metadataEditorContainer?: HTMLElement; protected _metadataEditorDisposeStore!: DisposableStore; - protected _metadataEditor?: CodeEditorWidget; + protected _metadataEditor?: CodeEditorWidget | DiffEditorWidget; constructor( readonly notebookEditor: INotebookTextDiffEditor, @@ -143,11 +146,22 @@ abstract class AbstractCellRenderer extends Disposable { } buildMetadataHeader(metadataHeaderContainer: HTMLElement): void { + let metadataChanged = this.cell.type === 'modified' && hash(this.cell.original?.metadata ?? {}) !== hash(this.cell.modified?.metadata ?? {}); this._foldingIndicator = DOM.append(metadataHeaderContainer, DOM.$('.metadata-folding-indicator')); + + if (metadataChanged) { + this.cell.foldingState = MetadataFoldingState.Expanded; + } + this._updateFoldingIcon(); const metadataStatus = DOM.append(metadataHeaderContainer, DOM.$('div.metadata-status')); - const metadataStatusSpan = DOM.append(metadataStatus, DOM.$('span')); - metadataStatusSpan.textContent = 'Metadata unchanged'; + this._metadataStatusSpan = DOM.append(metadataStatus, DOM.$('span')); + + if (metadataChanged) { + this._metadataStatusSpan.textContent = 'Metadata changed'; + } else { + this._metadataStatusSpan.textContent = 'Metadata unchanged'; + } this._register(this.notebookEditor.onMouseUp(e => { if (!e.event.target) { @@ -187,31 +201,7 @@ abstract class AbstractCellRenderer extends Disposable { if (!this._metadataEditorContainer || !this._metadataEditor) { // create editor this._metadataEditorContainer = DOM.append(this._metadataInfoContainer, DOM.$('.metadata-editor-container')); - - this._metadataEditor = this.instantiationService.createInstance(CodeEditorWidget, this._metadataEditorContainer, { - ...fixedEditorOptions, - dimension: { - width: this.notebookEditor.getLayoutInfo().width - 20, - height: 0 - } - }, {}); - - const mode = this.modeService.create('json'); - const content = JSON.stringify(this.cell.original!.metadata); - const edits = format(content, undefined, {}); - const metadataSource = applyEdits(content, edits); - const metadataModel = this.modelService.createModel(metadataSource, mode, undefined, true); - this._metadataEditor.setModel(metadataModel); - - this._layoutInfo.metadataHeight = this._metadataEditor.getContentHeight(); - this.layout({ metadataEditor: true }); - - this._register(this._metadataEditor.onDidContentSizeChange((e) => { - if (e.contentHeightChanged && this.cell.foldingState === MetadataFoldingState.Expanded) { - this._layoutInfo.metadataHeight = e.contentHeight; - this.layout({ metadataEditor: true }); - } - })); + this._buildMetadataEditor(); } else { this._layoutInfo.metadataHeight = this._metadataEditor.getContentHeight(); this.layout({ metadataEditor: true }); @@ -225,7 +215,69 @@ abstract class AbstractCellRenderer extends Disposable { } this._updateFoldingIcon(); + } + private _getFormatedJSON(metadata: NotebookCellMetadata) { + const content = JSON.stringify(metadata); + const edits = format(content, undefined, {}); + const metadataSource = applyEdits(content, edits); + + return metadataSource; + } + + private _buildMetadataEditor() { + if (this.cell.type === 'modified') { + const originalMetadataSource = this._getFormatedJSON(this.cell.original!.metadata || {}); + const modifiedMetadataSource = this._getFormatedJSON(this.cell.modified?.metadata || {}); + if (originalMetadataSource !== modifiedMetadataSource) { + this._metadataEditor = this.instantiationService.createInstance(DiffEditorWidget, this._metadataEditorContainer!, { + ...fixedDiffEditorOptions + }); + + const mode = this.modeService.create('json'); + const originalMetadataModel = this.modelService.createModel(originalMetadataSource, mode, undefined, true); + const modifiedMetadataModel = this.modelService.createModel(modifiedMetadataSource, mode, undefined, true); + this._metadataEditor.setModel({ + original: originalMetadataModel, + modified: modifiedMetadataModel + }); + + this._layoutInfo.metadataHeight = this._metadataEditor.getContentHeight(); + this.layout({ metadataEditor: true }); + + this._register(this._metadataEditor.onDidContentSizeChange((e) => { + if (e.contentHeightChanged && this.cell.foldingState === MetadataFoldingState.Expanded) { + this._layoutInfo.metadataHeight = e.contentHeight; + this.layout({ metadataEditor: true }); + } + })); + + return; + } + } + + this._metadataEditor = this.instantiationService.createInstance(CodeEditorWidget, this._metadataEditorContainer!, { + ...fixedEditorOptions, + dimension: { + width: this.notebookEditor.getLayoutInfo().width - 20, + height: 0 + } + }, {}); + + const mode = this.modeService.create('json'); + const originalMetadataSource = this._getFormatedJSON(this.cell.original!.metadata || {}); + const metadataModel = this.modelService.createModel(originalMetadataSource, mode, undefined, true); + this._metadataEditor.setModel(metadataModel); + + this._layoutInfo.metadataHeight = this._metadataEditor.getContentHeight(); + this.layout({ metadataEditor: true }); + + this._register(this._metadataEditor.onDidContentSizeChange((e) => { + if (e.contentHeightChanged && this.cell.foldingState === MetadataFoldingState.Expanded) { + this._layoutInfo.metadataHeight = e.contentHeight; + this.layout({ metadataEditor: true }); + } + })); } private _updateFoldingIcon() { @@ -580,6 +632,10 @@ export class ModifiedCell extends AbstractCellRenderer { } if (state.metadataEditor || state.outerWidth) { + if (this._metadataEditorContainer) { + this._metadataEditorContainer.style.height = `${this._layoutInfo.metadataHeight}px`; + } + this._metadataEditor?.layout({ width: this.notebookEditor.getLayoutInfo().width - 20, height: this._layoutInfo.metadataHeight diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css index ff4a478dfd4..e8d4555b720 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css @@ -16,10 +16,6 @@ width: 50%; } */ -/* .notebook-text-diff-editor { - margin: 8px; -} */ - .notebook-text-diff-editor .cell-body { display: flex; flex-direction: row; From 8b1f7dfddb598babac47e7dd4dc3506757de44fc Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 24 Aug 2020 11:51:59 -0700 Subject: [PATCH 477/736] put enhanced text diff behind a flag --- .../workbench/contrib/notebook/browser/notebook.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 86382efd0c8..869984bb140 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -242,7 +242,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri return undefined; } - if (originalInput instanceof DiffEditorInput) { + if (originalInput instanceof DiffEditorInput && this.configurationService.getValue('notebook.diff.enablePreview')) { return this._handleDiffEditorInput(originalInput, options, group); } From da818d13feb29a52ad3ed85bb29594c5fb94bef0 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 24 Aug 2020 20:59:23 +0200 Subject: [PATCH 478/736] debug: introduce debuggersAvailable context --- .../browser/breakpointEditorContribution.ts | 21 +++++++------ .../debug/browser/debug.contribution.ts | 30 +++++++++---------- .../browser/debugConfigurationManager.ts | 11 +++---- .../debug/browser/debugEditorActions.ts | 12 ++++---- .../contrib/debug/browser/welcomeView.ts | 16 +++++----- .../workbench/contrib/debug/common/debug.ts | 1 + 6 files changed, 45 insertions(+), 46 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts index 2a885219876..71aecb597c2 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts @@ -35,7 +35,6 @@ import { isSafari } from 'vs/base/browser/browser'; import { registerThemingParticipant, themeColorFromId } from 'vs/platform/theme/common/themeService'; import { registerColor } from 'vs/platform/theme/common/colorRegistry'; import { ILabelService } from 'vs/platform/label/common/label'; -import { debugAdapterRegisteredEmitter } from 'vs/workbench/contrib/debug/browser/debugConfigurationManager'; const $ = dom.$; @@ -169,20 +168,16 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi ) { this.breakpointWidgetVisible = CONTEXT_BREAKPOINT_WIDGET_VISIBLE.bindTo(contextKeyService); this.setDecorationsScheduler = new RunOnceScheduler(() => this.setDecorations(), 30); - const manager = this.debugService.getConfigurationManager(); - if (manager.hasDebuggers()) { - this.registerListeners(); - this.setDecorationsScheduler.schedule(); - } else { - this.toDispose.push(debugAdapterRegisteredEmitter.event(() => { - this.registerListeners(); - this.setDecorationsScheduler.schedule(); - })); - } + this.registerListeners(); + this.setDecorationsScheduler.schedule(); } private registerListeners(): void { this.toDispose.push(this.editor.onMouseDown(async (e: IEditorMouseEvent) => { + if (!this.debugService.getConfigurationManager().hasDebuggers()) { + return; + } + const data = e.target.detail as IMarginData; const model = this.editor.getModel(); if (!e.target.position || !model || e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || data.isAfterLines || !this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) { @@ -257,6 +252,10 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi * 2. When users click on line numbers, the breakpoint hint displays immediately, however it doesn't create the breakpoint unless users click on the left gutter. On a touch screen, it's hard to click on that small area. */ this.toDispose.push(this.editor.onMouseMove((e: IEditorMouseEvent) => { + if (!this.debugService.getConfigurationManager().hasDebuggers()) { + return; + } + let showBreakpointHintAtLineNumber = -1; const model = this.editor.getModel(); if (model && e.target.position && (e.target.type === MouseTargetType.GUTTER_GLYPH_MARGIN || e.target.type === MouseTargetType.GUTTER_LINE_NUMBERS) && this.debugService.getConfigurationManager().canSetBreakpointsIn(model) && diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 9ef3c011490..1700d0f93c5 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -17,7 +17,7 @@ import { CallStackView } from 'vs/workbench/contrib/debug/browser/callStackView' import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { IDebugService, VIEWLET_ID, DEBUG_PANEL_ID, CONTEXT_IN_DEBUG_MODE, INTERNAL_CONSOLE_OPTIONS_SCHEMA, - CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_DEBUG_UX, BREAKPOINT_EDITOR_CONTRIBUTION_ID, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, EDITOR_CONTRIBUTION_ID, + CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_DEBUG_UX, BREAKPOINT_EDITOR_CONTRIBUTION_ID, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, EDITOR_CONTRIBUTION_ID, CONTEXT_DEBUGGERS_AVAILABLE, } from 'vs/workbench/contrib/debug/common/debug'; import { StartAction, AddFunctionBreakpointAction, ConfigureAction, DisableAllBreakpointsAction, EnableAllBreakpointsAction, RemoveAllBreakpointsAction, RunAction, ReapplyBreakpointsAction, SelectAndStartAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { DebugToolBar } from 'vs/workbench/contrib/debug/browser/debugToolBar'; @@ -56,7 +56,6 @@ import { DebugEditorContribution } from 'vs/workbench/contrib/debug/browser/debu const registry = Registry.as(WorkbenchActionRegistryExtensions.WorkbenchActions); const debugCategory = nls.localize('debugCategory', "Debug"); const runCategroy = nls.localize('runCategory', "Run"); -// register service registerWorkbenchContributions(); registerColors(); registerCommandsAndActions(); @@ -64,8 +63,8 @@ registerDebugMenu(); registerEditorActions(); registerCommands(); registerDebugPanel(); -registry.registerWorkbenchAction(SyncActionDescriptor.from(StartAction, { primary: KeyCode.F5 }, CONTEXT_IN_DEBUG_MODE.toNegated()), 'Debug: Start Debugging', debugCategory); -registry.registerWorkbenchAction(SyncActionDescriptor.from(RunAction, { primary: KeyMod.CtrlCmd | KeyCode.F5, mac: { primary: KeyMod.WinCtrl | KeyCode.F5 } }), 'Run: Start Without Debugging', runCategroy); +registry.registerWorkbenchAction(SyncActionDescriptor.from(StartAction, { primary: KeyCode.F5 }, CONTEXT_IN_DEBUG_MODE.toNegated()), 'Debug: Start Debugging', debugCategory, CONTEXT_DEBUGGERS_AVAILABLE); +registry.registerWorkbenchAction(SyncActionDescriptor.from(RunAction, { primary: KeyMod.CtrlCmd | KeyCode.F5, mac: { primary: KeyMod.WinCtrl | KeyCode.F5 } }), 'Run: Start Without Debugging', runCategroy, CONTEXT_DEBUGGERS_AVAILABLE); registerSingleton(IDebugService, DebugService, true); registerDebugView(); @@ -102,18 +101,18 @@ function regsiterEditorContributions(): void { function registerCommandsAndActions(): void { - registry.registerWorkbenchAction(SyncActionDescriptor.from(ConfigureAction), 'Debug: Open launch.json', debugCategory); - registry.registerWorkbenchAction(SyncActionDescriptor.from(AddFunctionBreakpointAction), 'Debug: Add Function Breakpoint', debugCategory); - registry.registerWorkbenchAction(SyncActionDescriptor.from(ReapplyBreakpointsAction), 'Debug: Reapply All Breakpoints', debugCategory); - registry.registerWorkbenchAction(SyncActionDescriptor.from(RemoveAllBreakpointsAction), 'Debug: Remove All Breakpoints', debugCategory); - registry.registerWorkbenchAction(SyncActionDescriptor.from(EnableAllBreakpointsAction), 'Debug: Enable All Breakpoints', debugCategory); - registry.registerWorkbenchAction(SyncActionDescriptor.from(DisableAllBreakpointsAction), 'Debug: Disable All Breakpoints', debugCategory); - registry.registerWorkbenchAction(SyncActionDescriptor.from(SelectAndStartAction), 'Debug: Select and Start Debugging', debugCategory); - registry.registerWorkbenchAction(SyncActionDescriptor.from(ClearReplAction), 'Debug: Clear Console', debugCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.from(ConfigureAction), 'Debug: Open launch.json', debugCategory, CONTEXT_DEBUGGERS_AVAILABLE); + registry.registerWorkbenchAction(SyncActionDescriptor.from(AddFunctionBreakpointAction), 'Debug: Add Function Breakpoint', debugCategory, CONTEXT_DEBUGGERS_AVAILABLE); + registry.registerWorkbenchAction(SyncActionDescriptor.from(ReapplyBreakpointsAction), 'Debug: Reapply All Breakpoints', debugCategory, CONTEXT_DEBUGGERS_AVAILABLE); + registry.registerWorkbenchAction(SyncActionDescriptor.from(RemoveAllBreakpointsAction), 'Debug: Remove All Breakpoints', debugCategory, CONTEXT_DEBUGGERS_AVAILABLE); + registry.registerWorkbenchAction(SyncActionDescriptor.from(EnableAllBreakpointsAction), 'Debug: Enable All Breakpoints', debugCategory, CONTEXT_DEBUGGERS_AVAILABLE); + registry.registerWorkbenchAction(SyncActionDescriptor.from(DisableAllBreakpointsAction), 'Debug: Disable All Breakpoints', debugCategory, CONTEXT_DEBUGGERS_AVAILABLE); + registry.registerWorkbenchAction(SyncActionDescriptor.from(SelectAndStartAction), 'Debug: Select and Start Debugging', debugCategory, CONTEXT_DEBUGGERS_AVAILABLE); + registry.registerWorkbenchAction(SyncActionDescriptor.from(ClearReplAction), 'Debug: Clear Console', debugCategory, CONTEXT_DEBUGGERS_AVAILABLE); const registerDebugCommandPaletteItem = (id: string, title: string, when?: ContextKeyExpression, precondition?: ContextKeyExpression) => { MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - when, + when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, when), command: { id, title: `Debug: ${title}`, @@ -198,7 +197,7 @@ function registerCommandsAndActions(): void { title, icon: { dark: iconUri } }, - when, + when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, when), group: '9_debug', order }); @@ -449,10 +448,11 @@ function registerDebugPanel(): void { containerIcon: Codicon.debugConsole.classNames, canToggleVisibility: false, canMoveView: true, + when: CONTEXT_DEBUGGERS_AVAILABLE, ctorDescriptor: new SyncDescriptor(Repl), }], VIEW_CONTAINER); - registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenDebugConsoleAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Y }), 'View: Debug Console', nls.localize('view', "View")); + registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenDebugConsoleAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Y }), 'View: Debug Console', nls.localize('view', "View"), CONTEXT_DEBUGGERS_AVAILABLE); } function registerDebugView(): void { diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index f9c2f462c8b..b1d483a7755 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -21,7 +21,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState, IWorkspaceFoldersChangeEvent } 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, IDebugSession, IAdapterDescriptor, CONTEXT_DEBUG_CONFIGURATION_TYPE, IDebugAdapterFactory, IConfigPresentation } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugConfigurationProvider, ICompound, IDebugConfiguration, IConfig, IGlobalConfig, IConfigurationManager, ILaunch, IDebugAdapterDescriptorFactory, IDebugAdapter, IDebugSession, IAdapterDescriptor, CONTEXT_DEBUG_CONFIGURATION_TYPE, IDebugAdapterFactory, IConfigPresentation, CONTEXT_DEBUGGERS_AVAILABLE } from 'vs/workbench/contrib/debug/common/debug'; import { Debugger } from 'vs/workbench/contrib/debug/common/debugger'; import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -49,8 +49,6 @@ const DEBUG_SELECTED_ROOT = 'debug.selectedroot'; interface IDynamicPickItem { label: string, launch: ILaunch, config: IConfig } -export const debugAdapterRegisteredEmitter = new Emitter(); - export class ConfigurationManager implements IConfigurationManager { private debuggers: Debugger[]; private breakpointModeIdsSet = new Set(); @@ -64,6 +62,7 @@ export class ConfigurationManager implements IConfigurationManager { private adapterDescriptorFactories: IDebugAdapterDescriptorFactory[]; private debugAdapterFactories = new Map(); private debugConfigurationTypeContext: IContextKey; + private debuggersAvailable: IContextKey; private readonly _onDidRegisterDebugger = new Emitter(); constructor( @@ -87,6 +86,7 @@ export class ConfigurationManager implements IConfigurationManager { const previousSelectedRoot = this.storageService.get(DEBUG_SELECTED_ROOT, StorageScope.WORKSPACE); const previousSelectedLaunch = this.launches.find(l => l.uri.toString() === previousSelectedRoot); this.debugConfigurationTypeContext = CONTEXT_DEBUG_CONFIGURATION_TYPE.bindTo(contextKeyService); + this.debuggersAvailable = CONTEXT_DEBUGGERS_AVAILABLE.bindTo(contextKeyService); if (previousSelectedLaunch && previousSelectedLaunch.getConfigurationNames().length) { this.selectConfiguration(previousSelectedLaunch, this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE)); } else if (this.launches.length > 0) { @@ -97,11 +97,8 @@ export class ConfigurationManager implements IConfigurationManager { // debuggers registerDebugAdapterFactory(debugTypes: string[], debugAdapterLauncher: IDebugAdapterFactory): IDisposable { - const firstTimeRegistration = debugTypes.length && this.debugAdapterFactories.size === 0; debugTypes.forEach(debugType => this.debugAdapterFactories.set(debugType, debugAdapterLauncher)); - if (firstTimeRegistration) { - debugAdapterRegisteredEmitter.fire(); - } + this.debuggersAvailable.set(this.debugAdapterFactories.size > 0); return { dispose: () => { diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index 3d699d77e69..f8350cb13d5 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -9,7 +9,7 @@ import { Range } from 'vs/editor/common/core/range'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ServicesAccessor, registerEditorAction, EditorAction, IActionOptions } from 'vs/editor/browser/editorExtensions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, REPL_VIEW_ID, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, WATCH_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, REPL_VIEW_ID, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, WATCH_VIEW_ID, CONTEXT_DEBUGGERS_AVAILABLE } from 'vs/workbench/contrib/debug/common/debug'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { openBreakpointSource } from 'vs/workbench/contrib/debug/browser/breakpointsView'; @@ -27,7 +27,7 @@ class ToggleBreakpointAction extends EditorAction { id: TOGGLE_BREAKPOINT_ID, label: nls.localize('toggleBreakpointAction', "Debug: Toggle Breakpoint"), alias: 'Debug: Toggle Breakpoint', - precondition: undefined, + precondition: CONTEXT_DEBUGGERS_AVAILABLE, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyCode.F9, @@ -66,7 +66,7 @@ class ConditionalBreakpointAction extends EditorAction { id: TOGGLE_CONDITIONAL_BREAKPOINT_ID, label: nls.localize('conditionalBreakpointEditorAction', "Debug: Add Conditional Breakpoint..."), alias: 'Debug: Add Conditional Breakpoint...', - precondition: undefined + precondition: CONTEXT_DEBUGGERS_AVAILABLE }); } @@ -88,7 +88,7 @@ class LogPointAction extends EditorAction { id: ADD_LOG_POINT_ID, label: nls.localize('logPointEditorAction', "Debug: Add Logpoint..."), alias: 'Debug: Add Logpoint...', - precondition: undefined + precondition: CONTEXT_DEBUGGERS_AVAILABLE }); } @@ -339,7 +339,7 @@ class GoToNextBreakpointAction extends GoToBreakpointAction { id: 'editor.debug.action.goToNextBreakpoint', label: nls.localize('goToNextBreakpoint', "Debug: Go To Next Breakpoint"), alias: 'Debug: Go To Next Breakpoint', - precondition: undefined + precondition: CONTEXT_DEBUGGERS_AVAILABLE }); } } @@ -350,7 +350,7 @@ class GoToPreviousBreakpointAction extends GoToBreakpointAction { id: 'editor.debug.action.goToPreviousBreakpoint', label: nls.localize('goToPreviousBreakpoint', "Debug: Go To Previous Breakpoint"), alias: 'Debug: Go To Previous Breakpoint', - precondition: undefined + precondition: CONTEXT_DEBUGGERS_AVAILABLE }); } } diff --git a/src/vs/workbench/contrib/debug/browser/welcomeView.ts b/src/vs/workbench/contrib/debug/browser/welcomeView.ts index d9238fcd9e7..b0bad1df2f2 100644 --- a/src/vs/workbench/contrib/debug/browser/welcomeView.ts +++ b/src/vs/workbench/contrib/debug/browser/welcomeView.ts @@ -8,10 +8,10 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, RawContextKey, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { localize } from 'vs/nls'; import { StartAction, ConfigureAction, SelectAndStartAction } from 'vs/workbench/contrib/debug/browser/debugActions'; -import { IDebugService } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, CONTEXT_DEBUGGERS_AVAILABLE } from 'vs/workbench/contrib/debug/common/debug'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -109,30 +109,32 @@ const viewsRegistry = Registry.as(Extensions.ViewsRegistry); viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, { content: localize({ key: 'openAFileWhichCanBeDebugged', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] }, "[Open a file](command:{0}) which can be debugged or run.", isMacintosh ? OpenFileFolderAction.ID : OpenFileAction.ID), - when: CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR.toNegated() + when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR.toNegated()) }); let debugKeybindingLabel = ''; viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, { content: localize({ key: 'runAndDebugAction', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] }, "[Run and Debug{0}](command:{1})", debugKeybindingLabel, StartAction.ID), - preconditions: [CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR] + preconditions: [CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR], + when: CONTEXT_DEBUGGERS_AVAILABLE }); viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, { content: localize({ key: 'detectThenRunAndDebug', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] }, "[Show](command:{0}) all automatic debug configurations.", SelectAndStartAction.ID), - priority: ViewContentPriority.Lowest + priority: ViewContentPriority.Lowest, + when: CONTEXT_DEBUGGERS_AVAILABLE }); viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, { content: localize({ key: 'customizeRunAndDebug', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] }, "To customize Run and Debug [create a launch.json file](command:{0}).", ConfigureAction.ID), - when: WorkbenchStateContext.notEqualsTo('empty') + when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, WorkbenchStateContext.notEqualsTo('empty')) }); viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, { content: localize({ key: 'customizeRunAndDebugOpenFolder', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] }, "To customize Run and Debug, [open a folder](command:{0}) and create a launch.json file.", isMacintosh ? OpenFileFolderAction.ID : OpenFolderAction.ID), - when: WorkbenchStateContext.isEqualTo('empty') + when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, WorkbenchStateContext.isEqualTo('empty')) }); diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 702a54bde49..6467c93b443 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -59,6 +59,7 @@ export const CONTEXT_RESTART_FRAME_SUPPORTED = new RawContextKey('resta export const CONTEXT_JUMP_TO_CURSOR_SUPPORTED = new RawContextKey('jumpToCursorSupported', false); export const CONTEXT_STEP_INTO_TARGETS_SUPPORTED = new RawContextKey('stepIntoTargetsSupported', false); export const CONTEXT_BREAKPOINTS_EXIST = new RawContextKey('breakpointsExist', false); +export const CONTEXT_DEBUGGERS_AVAILABLE = new RawContextKey('debuggersAvailable', false); export const EDITOR_CONTRIBUTION_ID = 'editor.contrib.debug'; export const BREAKPOINT_EDITOR_CONTRIBUTION_ID = 'editor.contrib.breakpoint'; From 9e7b2b8bc2fe8e529a682e55522e885f3fd9e64a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 24 Aug 2020 21:09:04 +0200 Subject: [PATCH 479/736] change the scope to window --- src/vs/workbench/contrib/output/browser/output.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/output/browser/output.contribution.ts b/src/vs/workbench/contrib/output/browser/output.contribution.ts index 9a131ab24ef..472df16c4f5 100644 --- a/src/vs/workbench/contrib/output/browser/output.contribution.ts +++ b/src/vs/workbench/contrib/output/browser/output.contribution.ts @@ -322,7 +322,7 @@ Registry.as(ConfigurationExtensions.Configuration).regis type: 'boolean', description: nls.localize('output.smartScroll.enabled', "Enable/disable the ability of smart scrolling in the output view. Smart scrolling allows you to lock scrolling automatically when you click in the output view and unlocks when you click in the last line."), default: true, - scope: ConfigurationScope.APPLICATION, + scope: ConfigurationScope.WINDOW, tags: ['output'] } } From e85b3ba43bfc3b5455899ecc339e2ea885517782 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 24 Aug 2020 12:19:08 -0700 Subject: [PATCH 480/736] border around the whole cell. --- .../notebook/browser/diff/cellComponents.ts | 38 ++++++++----------- .../notebook/browser/diff/notebookDiff.css | 4 ++ .../browser/diff/notebookTextDiffEditor.ts | 10 ++--- 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index dcfb5b2b6b9..2516fa3f3a5 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -5,7 +5,7 @@ import * as DOM from 'vs/base/browser/dom'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { CellDiffViewModel, MetadataFoldingState } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; import { CellDiffRenderTemplate, CellDiffViewModelLayoutChangeEvent, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/common'; @@ -21,7 +21,7 @@ import { NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/noteb import { hash } from 'vs/base/common/hash'; -const fixedDiffEditorOptions: IEditorOptions = { +const fixedDiffEditorOptions: IDiffEditorOptions = { padding: { top: 12, bottom: 12 @@ -44,7 +44,9 @@ const fixedDiffEditorOptions: IEditorOptions = { glyphMargin: true, fixedOverflowWidgets: true, minimap: { enabled: false }, - renderValidationDecorations: 'on' + renderValidationDecorations: 'on', + enableSplitViewResizing: false, + renderIndicators: false }; const fixedEditorOptions: IEditorOptions = { @@ -111,6 +113,7 @@ abstract class AbstractCellRenderer extends Disposable { bodyMargin: 16 }; this._metadataEditorDisposeStore = new DisposableStore(); + this._register(this._metadataEditorDisposeStore); this.initData(); this.buildBody(templateData.container); this._register(cell.onDidLayoutChange(e => this.onDidLayoutChange(e))); @@ -227,7 +230,7 @@ abstract class AbstractCellRenderer extends Disposable { private _buildMetadataEditor() { if (this.cell.type === 'modified') { - const originalMetadataSource = this._getFormatedJSON(this.cell.original!.metadata || {}); + const originalMetadataSource = this._getFormatedJSON(this.cell.original?.metadata || {}); const modifiedMetadataSource = this._getFormatedJSON(this.cell.modified?.metadata || {}); if (originalMetadataSource !== modifiedMetadataSource) { this._metadataEditor = this.instantiationService.createInstance(DiffEditorWidget, this._metadataEditorContainer!, { @@ -265,7 +268,10 @@ abstract class AbstractCellRenderer extends Disposable { }, {}); const mode = this.modeService.create('json'); - const originalMetadataSource = this._getFormatedJSON(this.cell.original!.metadata || {}); + const originalMetadataSource = this._getFormatedJSON( + this.cell.type === 'insert' + ? this.cell.modified!.metadata || {} + : this.cell.original!.metadata || {}); const metadataModel = this.modelService.createModel(originalMetadataSource, mode, undefined, true); this._metadataEditor.setModel(metadataModel); @@ -409,11 +415,7 @@ export class DeletedCell extends AbstractCellRenderer { height: editorHeight } }, {}); - - if (this._diagonalFill) { - this._layoutInfo.editorHeight = editorHeight; - // this._diagonalFill.style.height = `${this._diffEditorContainer.clientHeight}px`; - } + this._layoutInfo.editorHeight = editorHeight; this._register(this._editor.onDidContentSizeChange((e) => { if (e.contentHeightChanged) { @@ -453,10 +455,6 @@ export class DeletedCell extends AbstractCellRenderer { }); } - if (this._diagonalFill) { - // this._diagonalFill.style.height = `${this._diffEditorContainer.clientHeight}px`; - } - this.notebookEditor.layoutNotebookCell(this.cell, this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight + this._layoutInfo.bodyMargin); } @@ -498,10 +496,6 @@ export class InsertCell extends AbstractCellRenderer { this._layoutInfo.editorHeight = editorHeight; - if (this._diagonalFill) { - this._diagonalFill.style.height = `${this._diffEditorContainer.clientHeight}px`; - } - this._register(this._editor.onDidContentSizeChange((e) => { if (e.contentHeightChanged) { this._layoutInfo.editorHeight = e.contentHeight; @@ -534,16 +528,16 @@ export class InsertCell extends AbstractCellRenderer { } if (state.metadataEditor || state.outerWidth) { + if (this._metadataEditorContainer) { + this._metadataEditorContainer.style.height = `${this._layoutInfo.metadataHeight}px`; + } + this._metadataEditor?.layout({ width: this.notebookEditor.getLayoutInfo().width - 20, height: this._layoutInfo.metadataHeight }); } - if (this._diagonalFill) { - this._diagonalFill.style.height = `${this._diffEditorContainer.clientHeight}px`; - } - this.notebookEditor.layoutNotebookCell(this.cell, this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight + this._layoutInfo.bodyMargin); } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css index e8d4555b720..f075c903749 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css @@ -22,6 +22,10 @@ margin: 8px; } +.notebook-text-diff-editor .cell-body.right { + flex-direction: row-reverse; +} + .notebook-text-diff-editor .cell-body .diagonal-fill { display: none; width: 50%; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index bc6413d2b4a..b3a6db55f6f 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -288,16 +288,12 @@ export class NotebookTextDiffEditor extends BaseEditor implements INotebookTextD registerThemingParticipant((theme, collector) => { const cellBorderColor = theme.getColor(notebookCellBorder); if (cellBorderColor) { - collector.addRule(`.notebook-text-diff-editor .source-container { border: 1px solid ${cellBorderColor};}`); + collector.addRule(`.notebook-text-diff-editor .cell-body { border: 1px solid ${cellBorderColor};}`); collector.addRule(`.notebook-text-diff-editor .metadata-editor-container { - border-left: 1px solid ${cellBorderColor}; - border-right: 1px solid ${cellBorderColor}; - border-bottom: 1px solid ${cellBorderColor}; + border-top: 1px solid ${cellBorderColor} }`); collector.addRule(`.notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container { - border-left: 1px solid ${cellBorderColor}; - border-right: 1px solid ${cellBorderColor}; - border-bottom: 1px solid ${cellBorderColor}; + border-top: 1px solid ${cellBorderColor}; }`); } From 4ffd7a9e1b02a31dc63deefa729bf34d64a65da2 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 24 Aug 2020 12:24:18 -0700 Subject: [PATCH 481/736] :lipstick: --- .../workbench/contrib/notebook/browser/diff/cellComponents.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index 2516fa3f3a5..f65fa0e43ce 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -163,7 +163,7 @@ abstract class AbstractCellRenderer extends Disposable { if (metadataChanged) { this._metadataStatusSpan.textContent = 'Metadata changed'; } else { - this._metadataStatusSpan.textContent = 'Metadata unchanged'; + this._metadataStatusSpan.textContent = 'Metadata'; } this._register(this.notebookEditor.onMouseUp(e => { From 69c1c79b81fe93f25c628cd8ea4e332c7956adc5 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 24 Aug 2020 13:34:46 -0700 Subject: [PATCH 482/736] :guard: compilation. --- .../notebook/browser/diff/notebookTextDiffEditor.ts | 12 +++++------- .../notebook/common/services/notebookSimpleWorker.ts | 2 +- .../common/services/notebookWorkerServiceImpl.ts | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index b3a6db55f6f..6fbc561b672 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -8,8 +8,7 @@ import * as DOM from 'vs/base/browser/dom'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { EditorOptions } from 'vs/workbench/common/editor'; +import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; import { notebookCellBorder, NotebookEditorWidget, notebookOutputContainerColor } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { NotebookDiffEditorInput } from '../notebookDiffEditorInput'; @@ -30,8 +29,9 @@ import { INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/d import { Emitter } from 'vs/base/common/event'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { NotebookDiffEditorEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; -export class NotebookTextDiffEditor extends BaseEditor implements INotebookTextDiffEditor { +export class NotebookTextDiffEditor extends EditorPane implements INotebookTextDiffEditor { static readonly ID: string = 'workbench.editor.notebookTextDiffEditor'; private _rootElement!: HTMLElement; @@ -121,10 +121,8 @@ export class NotebookTextDiffEditor extends BaseEditor implements INotebookTextD })); } - async setInput(input: NotebookDiffEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { - // const group = this.group!; - - await super.setInput(input, options, token); + async setInput(input: NotebookDiffEditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { + await super.setInput(input, options, context, token); const model = await input.resolve(); if (model === null) { diff --git a/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts b/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts index 65870613c73..b6db9d9f38b 100644 --- a/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts +++ b/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts @@ -114,7 +114,7 @@ export class NotebookEditorSimpleWorker implements IRequestHandler, IDisposable public acceptNewModel(uri: string, data: NotebookDataDto): void { this._models[uri] = new MirrorNotebookDocument(URI.parse(uri), data.cells.map(dto => new MirrorCell( - (dto as IMainCellDto).handle, + (dto as unknown as IMainCellDto).handle, dto.source, dto.language, dto.cellKind, diff --git a/src/vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl.ts b/src/vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl.ts index 8f12d9ccda4..b209d8c7fd4 100644 --- a/src/vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl.ts @@ -99,7 +99,7 @@ export class NotebookEditorModelManager extends Disposable { cells: model.cells.map(cell => ({ handle: cell.handle, uri: cell.uri, - source: cell.textBuffer.getLinesContent(), + source: cell.getValue(), eol: cell.textBuffer.getEOL(), language: cell.language, cellKind: cell.cellKind, From 1741786554dccb7995e9d3b168017193c4914cd3 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 24 Aug 2020 13:54:30 -0700 Subject: [PATCH 483/736] show language info in metadata --- .../contrib/notebook/browser/diff/cellComponents.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index f65fa0e43ce..65fbd162cec 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -220,8 +220,12 @@ abstract class AbstractCellRenderer extends Disposable { this._updateFoldingIcon(); } - private _getFormatedJSON(metadata: NotebookCellMetadata) { - const content = JSON.stringify(metadata); + private _getFormatedJSON(metadata: NotebookCellMetadata, language?: string) { + const content = JSON.stringify({ + language, + ...metadata + }); + const edits = format(content, undefined, {}); const metadataSource = applyEdits(content, edits); @@ -230,8 +234,8 @@ abstract class AbstractCellRenderer extends Disposable { private _buildMetadataEditor() { if (this.cell.type === 'modified') { - const originalMetadataSource = this._getFormatedJSON(this.cell.original?.metadata || {}); - const modifiedMetadataSource = this._getFormatedJSON(this.cell.modified?.metadata || {}); + const originalMetadataSource = this._getFormatedJSON(this.cell.original?.metadata || {}, this.cell.original?.language); + const modifiedMetadataSource = this._getFormatedJSON(this.cell.modified?.metadata || {}, this.cell.modified?.language); if (originalMetadataSource !== modifiedMetadataSource) { this._metadataEditor = this.instantiationService.createInstance(DiffEditorWidget, this._metadataEditorContainer!, { ...fixedDiffEditorOptions From 2cf4c20d688da3e734e2471890d5a6ae17f763a8 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 24 Aug 2020 14:52:35 -0700 Subject: [PATCH 484/736] style update for better visibility --- .../notebook/browser/diff/cellComponents.ts | 38 +++++++++---------- .../contrib/notebook/browser/diff/common.ts | 2 + .../notebook/browser/diff/notebookDiff.css | 1 - .../browser/diff/notebookTextDiffEditor.ts | 2 +- 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index 65fbd162cec..a279e9e6f04 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -8,7 +8,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { CellDiffViewModel, MetadataFoldingState } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; -import { CellDiffRenderTemplate, CellDiffViewModelLayoutChangeEvent, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/common'; +import { CellDiffRenderTemplate, CellDiffViewModelLayoutChangeEvent, DIFF_CELL_MARGIN, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/common'; import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; @@ -46,7 +46,8 @@ const fixedDiffEditorOptions: IDiffEditorOptions = { minimap: { enabled: false }, renderValidationDecorations: 'on', enableSplitViewResizing: false, - renderIndicators: false + renderIndicators: false, + renderLineHighlight: 'none' }; const fixedEditorOptions: IEditorOptions = { @@ -72,7 +73,8 @@ const fixedEditorOptions: IEditorOptions = { glyphMargin: false, fixedOverflowWidgets: true, minimap: { enabled: false }, - renderValidationDecorations: 'on' + renderValidationDecorations: 'on', + renderLineHighlight: 'none' }; abstract class AbstractCellRenderer extends Disposable { @@ -110,7 +112,7 @@ abstract class AbstractCellRenderer extends Disposable { editorMargin: 0, metadataHeight: 0, metadataStatusHeight: 25, - bodyMargin: 16 + bodyMargin: 32 }; this._metadataEditorDisposeStore = new DisposableStore(); this._register(this._metadataEditorDisposeStore); @@ -152,9 +154,6 @@ abstract class AbstractCellRenderer extends Disposable { let metadataChanged = this.cell.type === 'modified' && hash(this.cell.original?.metadata ?? {}) !== hash(this.cell.modified?.metadata ?? {}); this._foldingIndicator = DOM.append(metadataHeaderContainer, DOM.$('.metadata-folding-indicator')); - if (metadataChanged) { - this.cell.foldingState = MetadataFoldingState.Expanded; - } this._updateFoldingIcon(); const metadataStatus = DOM.append(metadataHeaderContainer, DOM.$('div.metadata-status')); @@ -162,6 +161,7 @@ abstract class AbstractCellRenderer extends Disposable { if (metadataChanged) { this._metadataStatusSpan.textContent = 'Metadata changed'; + this._metadataStatusSpan.style.fontWeight = 'bold'; } else { this._metadataStatusSpan.textContent = 'Metadata'; } @@ -266,7 +266,7 @@ abstract class AbstractCellRenderer extends Disposable { this._metadataEditor = this.instantiationService.createInstance(CodeEditorWidget, this._metadataEditorContainer!, { ...fixedEditorOptions, dimension: { - width: this.notebookEditor.getLayoutInfo().width - 20, + width: this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN, height: 0 } }, {}); @@ -335,7 +335,7 @@ export class UnchangedCell extends AbstractCellRenderer { this._editor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, { ...fixedEditorOptions, dimension: { - width: this.notebookEditor.getLayoutInfo().width - 20, + width: this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN, height: editorHeight } }, {}); @@ -368,14 +368,14 @@ export class UnchangedCell extends AbstractCellRenderer { layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean }) { if (state.editorHeight || state.outerWidth) { this._editor.layout({ - width: this.notebookEditor.getLayoutInfo().width - 20, + width: this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN, height: this._layoutInfo.editorHeight }); } if (state.metadataEditor || state.outerWidth) { this._metadataEditor?.layout({ - width: this.notebookEditor.getLayoutInfo().width - 20, + width: this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN, height: this._layoutInfo.metadataHeight }); } @@ -415,7 +415,7 @@ export class DeletedCell extends AbstractCellRenderer { this._editor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, { ...fixedEditorOptions, dimension: { - width: (this.notebookEditor.getLayoutInfo().width - 20) / 2 - 18, + width: (this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN) / 2 - 18, height: editorHeight } }, {}); @@ -447,14 +447,14 @@ export class DeletedCell extends AbstractCellRenderer { layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean }) { if (state.editorHeight || state.outerWidth) { this._editor.layout({ - width: (this.notebookEditor.getLayoutInfo().width - 20) / 2 - 18, + width: (this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN) / 2 - 18, height: this._layoutInfo.editorHeight }); } if (state.metadataEditor || state.outerWidth) { this._metadataEditor?.layout({ - width: (this.notebookEditor.getLayoutInfo().width - 20) / 2 - 18, + width: (this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN) / 2 - 18, height: this._layoutInfo.metadataHeight }); } @@ -493,7 +493,7 @@ export class InsertCell extends AbstractCellRenderer { this._editor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, { ...fixedEditorOptions, dimension: { - width: (this.notebookEditor.getLayoutInfo().width - 20) / 2 - 18, + width: (this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN) / 2 - 18, height: editorHeight } }, {}); @@ -526,7 +526,7 @@ export class InsertCell extends AbstractCellRenderer { layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean }) { if (state.editorHeight || state.outerWidth) { this._editor.layout({ - width: (this.notebookEditor.getLayoutInfo().width - 20) / 2 - 18, + width: (this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN) / 2 - 18, height: this._layoutInfo.editorHeight }); } @@ -537,7 +537,7 @@ export class InsertCell extends AbstractCellRenderer { } this._metadataEditor?.layout({ - width: this.notebookEditor.getLayoutInfo().width - 20, + width: this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN, height: this._layoutInfo.metadataHeight }); } @@ -579,7 +579,7 @@ export class ModifiedCell extends AbstractCellRenderer { }); this._editor.layout({ - width: this.notebookEditor.getLayoutInfo().width - 20, + width: this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN, height: editorHeight }); @@ -635,7 +635,7 @@ export class ModifiedCell extends AbstractCellRenderer { } this._metadataEditor?.layout({ - width: this.notebookEditor.getLayoutInfo().width - 20, + width: this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN, height: this._layoutInfo.metadataHeight }); } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/common.ts b/src/vs/workbench/contrib/notebook/browser/diff/common.ts index ea439226ed7..f1df35485c8 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/common.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/common.ts @@ -24,3 +24,5 @@ export interface CellDiffViewModelLayoutChangeEvent { font?: BareFontInfo; outerWidth?: number; } + +export const DIFF_CELL_MARGIN = 16; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css index f075c903749..e4a219bcf38 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css @@ -19,7 +19,6 @@ .notebook-text-diff-editor .cell-body { display: flex; flex-direction: row; - margin: 8px; } .notebook-text-diff-editor .cell-body.right { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index 6fbc561b672..1dedeca5368 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -9,7 +9,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; -import { notebookCellBorder, NotebookEditorWidget, notebookOutputContainerColor } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; +import { notebookCellBorder, NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { NotebookDiffEditorInput } from '../notebookDiffEditorInput'; import { CancellationToken } from 'vs/base/common/cancellation'; From 9411392d0955a6559fce541dce3555c84c22cb85 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 24 Aug 2020 14:52:57 -0700 Subject: [PATCH 485/736] differenciate content change and metadata change. --- .../browser/diff/notebookTextDiffEditor.ts | 38 ++++++++++--------- .../contrib/notebook/common/notebookCommon.ts | 2 +- .../common/services/notebookSimpleWorker.ts | 34 ++++++++++++----- 3 files changed, 46 insertions(+), 28 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index 1dedeca5368..d86af68ee5b 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -25,7 +25,7 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { getZoomLevel } from 'vs/base/browser/browser'; import { NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/common'; +import { DIFF_CELL_MARGIN, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/common'; import { Emitter } from 'vs/base/common/event'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { NotebookDiffEditorEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; @@ -144,14 +144,25 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD const change = cellChanges[i]; // common cells - cellDiffViewModels.push(...originalModel.cells.slice(originalCellIndex, change.originalStart).map(cell => { - return new CellDiffViewModel( - cell, - undefined, - 'unchanged', - this._eventDispatcher! - ); - })); + for (let j = 0; j < change.originalStart - originalCellIndex; j++) { + const originalCell = originalModel.cells[originalCellIndex + j]; + const modifiedCell = modifiedModel.cells[modifiedCellIndex + j]; + if (originalCell.getHashValue() === modifiedCell.getHashValue()) { + cellDiffViewModels.push(new CellDiffViewModel( + originalCell, + undefined, + 'unchanged', + this._eventDispatcher! + )); + } else { + cellDiffViewModels.push(new CellDiffViewModel( + originalCell, + modifiedCell, + 'modified', + this._eventDispatcher! + )); + } + } // modified cells const modifiedLen = Math.min(change.originalLength, change.modifiedLength); @@ -287,9 +298,6 @@ registerThemingParticipant((theme, collector) => { const cellBorderColor = theme.getColor(notebookCellBorder); if (cellBorderColor) { collector.addRule(`.notebook-text-diff-editor .cell-body { border: 1px solid ${cellBorderColor};}`); - collector.addRule(`.notebook-text-diff-editor .metadata-editor-container { - border-top: 1px solid ${cellBorderColor} - }`); collector.addRule(`.notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container { border-top: 1px solid ${cellBorderColor}; }`); @@ -309,9 +317,5 @@ registerThemingParticipant((theme, collector) => { } `); - const containerBackground = theme.getColor(notebookOutputContainerColor); - if (containerBackground) { - collector.addRule(`.notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container { background-color: ${containerBackground}; }`); - } - + collector.addRule(`.notebook-text-diff-editor .cell-body { margin: ${DIFF_CELL_MARGIN}px; }`); }); diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 46ba0e5442d..2ad327e4472 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -755,7 +755,7 @@ export class CellSequence implements ISequence { export interface INotebookDiffResult { cellsDiff: IDiffResult, - linesDiff: { originalCellhandle: number, modifiedCellhandle: number, lineChanges: editorCommon.ILineChange[] }[]; + linesDiff?: { originalCellhandle: number, modifiedCellhandle: number, lineChanges: editorCommon.ILineChange[] }[]; } export const DisplayOrderKey = 'notebook.displayOrder'; export const CellToolbarLocKey = 'notebook.cellToolbarLocation'; diff --git a/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts b/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts index b6db9d9f38b..547f3678545 100644 --- a/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts +++ b/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts @@ -11,8 +11,6 @@ import * as model from 'vs/editor/common/model'; import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder'; import { CellKind, ICellDto2, IMainCellDto, INotebookDiffResult, IProcessedOutput, NotebookCellMetadata, NotebookDataDto, NotebookDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { Range } from 'vs/editor/common/core/range'; -import { DiffComputer } from 'vs/editor/common/diff/diffComputer'; -import * as editorCommon from 'vs/editor/common/editorCommon'; import { EditorWorkerHost } from 'vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl'; class MirrorCell { @@ -31,8 +29,16 @@ class MirrorCell { return this._textBuffer; } - private _hash: number | null = null; + private _primaryKey?: number | null = null; + primaryKey(): number | null { + if (this._primaryKey === undefined) { + this._primaryKey = hash(this.getValue()); + } + return this._primaryKey; + } + + private _hash: number | null = null; constructor( readonly handle: number, @@ -59,13 +65,21 @@ class MirrorCell { } } - getHashValue(): number { + getComparisonValue(): number { + if (this._primaryKey !== null) { + return this._primaryKey!; + } + + this._hash = hash([hash(this.getValue()), this.metadata]); + return this._hash; + } + + getHashValue() { if (this._hash !== null) { return this._hash; } - this._hash = hash([hash(this.getValue()), this.metadata]); - // this._hash = hash(this.getValue()); + this._hash = hash([hash(this.getValue()), this.language, this.metadata]); return this._hash; } } @@ -88,7 +102,7 @@ export class CellSequence implements ISequence { getElements(): string[] | number[] | Int32Array { const hashValue = new Int32Array(this.textModel.cells.length); for (let i = 0; i < this.textModel.cells.length; i++) { - hashValue[i] = this.textModel.cells[i].getHashValue(); + hashValue[i] = this.textModel.cells[i].getComparisonValue(); } return hashValue; @@ -137,7 +151,7 @@ export class NotebookEditorSimpleWorker implements IRequestHandler, IDisposable const diff = new LcsDiff(new CellSequence(original), new CellSequence(modified)); const diffResult = diff.ComputeDiff(false); - let cellLineChanges: { originalCellhandle: number, modifiedCellhandle: number, lineChanges: editorCommon.ILineChange[] }[] = []; + /* let cellLineChanges: { originalCellhandle: number, modifiedCellhandle: number, lineChanges: editorCommon.ILineChange[] }[] = []; diffResult.changes.forEach(change => { if (change.modifiedLength === 0) { @@ -185,10 +199,10 @@ export class NotebookEditorSimpleWorker implements IRequestHandler, IDisposable } }); - + */ return { cellsDiff: diffResult, - linesDiff: cellLineChanges + // linesDiff: cellLineChanges }; } From be6d3c4dda283e4b2fbecf1345f6e9d0628a2382 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 24 Aug 2020 15:51:47 -0700 Subject: [PATCH 486/736] Fixing saving of webview view state across restarts --- .../webviewView/browser/webviewViewPane.ts | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts b/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts index d1c1d95b238..31ce142a161 100644 --- a/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts +++ b/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts @@ -7,7 +7,6 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; import { toDisposable } from 'vs/base/common/lifecycle'; import { setImmediate } from 'vs/base/common/platform'; -import { generateUuid } from 'vs/base/common/uuid'; import { MenuId } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -16,18 +15,21 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IProgressService } from 'vs/platform/progress/common/progress'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; +import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IWebviewService, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { IWebviewViewService } from 'vs/workbench/contrib/webviewView/browser/webviewViewService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; - declare const ResizeObserver: any; +const webviewStateKey = 'webviewState'; + export class WebviewViewPane extends ViewPane { private _webview?: WebviewOverlay; @@ -36,6 +38,9 @@ export class WebviewViewPane extends ViewPane { private _container?: HTMLElement; private _resizeObserver?: any; + private readonly memento: Memento; + private readonly viewState: MementoObject; + constructor( options: IViewletViewOptions, @IKeybindingService keybindingService: IKeybindingService, @@ -47,6 +52,7 @@ export class WebviewViewPane extends ViewPane { @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, @ITelemetryService telemetryService: ITelemetryService, + @IStorageService storageService: IStorageService, @IExtensionService private readonly extensionService: IExtensionService, @IProgressService private readonly progressService: IProgressService, @IWebviewService private readonly webviewService: IWebviewService, @@ -54,6 +60,9 @@ export class WebviewViewPane extends ViewPane { ) { super({ ...options, titleMenuId: MenuId.ViewTitle }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); + this.memento = new Memento(`webviewView.${this.id}`, storageService); + this.viewState = this.memento.getMemento(StorageScope.WORKSPACE); + this._register(this.onDidChangeBodyVisibility(() => this.updateTreeVisibility())); this.updateTreeVisibility(); } @@ -96,6 +105,15 @@ export class WebviewViewPane extends ViewPane { } } + public saveState() { + if (this._webview) { + this.viewState[webviewStateKey] = this._webview.state; + } + + this.memento.saveMemento(); + super.saveState(); + } + protected layoutBody(height: number, width: number): void { super.layoutBody(height, width); @@ -121,13 +139,19 @@ export class WebviewViewPane extends ViewPane { if (!this._activated) { this._activated = true; - const webview = this.webviewService.createWebviewOverlay(generateUuid(), {}, {}, undefined); + const webviewId = `webviewView-${this.id.replace(/[^a-z0-9]/gi, '-')}`.toLowerCase(); + const webview = this.webviewService.createWebviewOverlay(webviewId, {}, {}, undefined); + webview.state = this.viewState['webviewState']; this._webview = webview; this._register(toDisposable(() => { this._webview?.release(this); })); + this._register(webview.onDidUpdateState(() => { + this.viewState[webviewStateKey] = webview.state; + })); + const source = this._register(new CancellationTokenSource()); this.withProgress(async () => { @@ -149,5 +173,3 @@ export class WebviewViewPane extends ViewPane { return this.progressService.withProgress({ location: this.id, delay: 500 }, task); } } - - From 13cfaaffef1c9b839d863cc2b59b8dfa104d414f Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 24 Aug 2020 16:38:05 -0700 Subject: [PATCH 487/736] hide diff editor overview --- .../notebook/browser/diff/cellComponents.ts | 19 ++++++----- .../browser/diff/celllDiffViewModel.ts | 12 ++++++- .../notebook/browser/diff/notebookDiff.css | 32 +++++++++---------- 3 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index a279e9e6f04..08b835e0b67 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -241,6 +241,8 @@ abstract class AbstractCellRenderer extends Disposable { ...fixedDiffEditorOptions }); + DOM.addClass(this._metadataEditorContainer!, 'diff'); + const mode = this.modeService.create('json'); const originalMetadataModel = this.modelService.createModel(originalMetadataSource, mode, undefined, true); const modifiedMetadataModel = this.modelService.createModel(modifiedMetadataSource, mode, undefined, true); @@ -266,7 +268,7 @@ abstract class AbstractCellRenderer extends Disposable { this._metadataEditor = this.instantiationService.createInstance(CodeEditorWidget, this._metadataEditorContainer!, { ...fixedEditorOptions, dimension: { - width: this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN, + width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true), height: 0 } }, {}); @@ -368,14 +370,14 @@ export class UnchangedCell extends AbstractCellRenderer { layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean }) { if (state.editorHeight || state.outerWidth) { this._editor.layout({ - width: this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN, + width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true), height: this._layoutInfo.editorHeight }); } if (state.metadataEditor || state.outerWidth) { this._metadataEditor?.layout({ - width: this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN, + width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true), height: this._layoutInfo.metadataHeight }); } @@ -447,14 +449,14 @@ export class DeletedCell extends AbstractCellRenderer { layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean }) { if (state.editorHeight || state.outerWidth) { this._editor.layout({ - width: (this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN) / 2 - 18, + width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false), height: this._layoutInfo.editorHeight }); } if (state.metadataEditor || state.outerWidth) { this._metadataEditor?.layout({ - width: (this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN) / 2 - 18, + width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false), height: this._layoutInfo.metadataHeight }); } @@ -526,7 +528,7 @@ export class InsertCell extends AbstractCellRenderer { layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean }) { if (state.editorHeight || state.outerWidth) { this._editor.layout({ - width: (this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN) / 2 - 18, + width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false), height: this._layoutInfo.editorHeight }); } @@ -537,7 +539,7 @@ export class InsertCell extends AbstractCellRenderer { } this._metadataEditor?.layout({ - width: this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN, + width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true), height: this._layoutInfo.metadataHeight }); } @@ -577,6 +579,7 @@ export class ModifiedCell extends AbstractCellRenderer { this._editor = this.instantiationService.createInstance(DiffEditorWidget, this._editorContainer, { ...fixedDiffEditorOptions }); + DOM.addClass(this._editorContainer, 'diff'); this._editor.layout({ width: this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN, @@ -635,7 +638,7 @@ export class ModifiedCell extends AbstractCellRenderer { } this._metadataEditor?.layout({ - width: this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN, + width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true), height: this._layoutInfo.metadataHeight }); } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel.ts b/src/vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel.ts index 602509f535b..440356733fe 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel.ts @@ -7,7 +7,9 @@ import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/mode import { NotebookDiffEditorEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { CellDiffViewModelLayoutChangeEvent } from 'vs/workbench/contrib/notebook/browser/diff/common'; +import { CellDiffViewModelLayoutChangeEvent, DIFF_CELL_MARGIN } from 'vs/workbench/contrib/notebook/browser/diff/common'; +import { NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; export enum MetadataFoldingState { Expanded, @@ -33,4 +35,12 @@ export class CellDiffViewModel extends Disposable { this._layoutInfoEmitter.fire({ outerWidth: e.value.width }); })); } + + getComputedCellContainerWidth(layoutInfo: NotebookLayoutInfo, diffEditor: boolean, fullWidth: boolean) { + if (fullWidth) { + return layoutInfo.width - 2 * DIFF_CELL_MARGIN + (diffEditor ? DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH : 0) - 2; + } + + return (layoutInfo.width - 2 * DIFF_CELL_MARGIN + (diffEditor ? DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH : 0)) / 2 - 18 - 2; + } } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css index e4a219bcf38..b73ba200e05 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css @@ -32,32 +32,32 @@ .notebook-text-diff-editor .cell-body .cell-diff-editor-container { width: 100%; + overflow: hidden; +} + +.notebook-text-diff-editor .cell-body .cell-diff-editor-container .editor-container.diff { + /** 100% + diffOverviewWidth */ + width: calc(100% + 30px); +} + +.notebook-text-diff-editor .cell-body .cell-diff-editor-container .editor-container.diff .monaco-diff-editor .diffOverview { + display: none; +} + +.notebook-text-diff-editor .cell-body .cell-diff-editor-container .editor-container { + box-sizing: border-box; } .notebook-text-diff-editor .cell-body.left .cell-diff-editor-container, .notebook-text-diff-editor .cell-body.right .cell-diff-editor-container { display: inline-block; + width: 50%; } .notebook-text-diff-editor .cell-body.left .diagonal-fill, .notebook-text-diff-editor .cell-body.right .diagonal-fill { display: inline-block; -} - -.notebook-text-diff-editor .cell-body.left .cell-diff-editor-container { - width: calc(50% - 18px); -} - -.notebook-text-diff-editor .cell-body.left .diagonal-fill { - width: calc(50% + 18px); -} - -.notebook-text-diff-editor .cell-body.right .cell-diff-editor-container { - width: calc(50% + 18px); -} - -.notebook-text-diff-editor .cell-body.right .diagonal-fill { - width: calc(50% - 18px); + width: 50%; } .notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container { From 3ab605c4dea5f3fbd2155ddb54729c9f45b31ccf Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 24 Aug 2020 17:15:03 -0700 Subject: [PATCH 488/736] Fix notebook argument processor for cells --- src/vs/workbench/api/common/extHostNotebook.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index d7b304c7eee..b24e33fde16 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -930,7 +930,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN this._proxy = mainContext.getProxy(MainContext.MainThreadNotebook); commands.registerArgumentProcessor({ - processArgument: arg => { + // Serialized INotebookCellActionContext + processArgument: (arg) => { if (arg && arg.$mid === 12) { const documentHandle = arg.notebookEditor?.notebookHandle; const cellHandle = arg.cell.handle; @@ -939,7 +940,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN if (value[1].editor.notebookData.handle === documentHandle) { const cell = value[1].editor.notebookData.getCell(cellHandle); if (cell) { - return cell; + return cell.cell; } } } From 7057d8c0de6ab018be1eeb2b7c2091793f46d3cf Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 24 Aug 2020 17:37:37 -0700 Subject: [PATCH 489/736] hide diff overview in metadata editor --- .../contrib/notebook/browser/diff/cellComponents.ts | 6 +----- .../contrib/notebook/browser/diff/notebookDiff.css | 3 +++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index 08b835e0b67..3bc00a3b9ce 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -635,12 +635,8 @@ export class ModifiedCell extends AbstractCellRenderer { if (state.metadataEditor || state.outerWidth) { if (this._metadataEditorContainer) { this._metadataEditorContainer.style.height = `${this._layoutInfo.metadataHeight}px`; + this._metadataEditor?.layout(); } - - this._metadataEditor?.layout({ - width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true), - height: this._layoutInfo.metadataHeight - }); } this.notebookEditor.layoutNotebookCell(this.cell, diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css index b73ba200e05..af0b26142a3 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css @@ -35,15 +35,18 @@ overflow: hidden; } +.notebook-text-diff-editor .cell-body .cell-diff-editor-container .metadata-editor-container.diff, .notebook-text-diff-editor .cell-body .cell-diff-editor-container .editor-container.diff { /** 100% + diffOverviewWidth */ width: calc(100% + 30px); } +.notebook-text-diff-editor .cell-body .cell-diff-editor-container .metadata-editor-container .monaco-diff-editor .diffOverview, .notebook-text-diff-editor .cell-body .cell-diff-editor-container .editor-container.diff .monaco-diff-editor .diffOverview { display: none; } +.notebook-text-diff-editor .cell-body .cell-diff-editor-container .metadata-editor-container, .notebook-text-diff-editor .cell-body .cell-diff-editor-container .editor-container { box-sizing: border-box; } From f580e1d30fc4732fd97857a2773ad916ec24e2f2 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 24 Aug 2020 18:38:35 -0700 Subject: [PATCH 490/736] diff editor for outputs --- .../notebook/browser/diff/cellComponents.ts | 375 ++++++++++++++---- .../browser/diff/celllDiffViewModel.ts | 8 +- .../notebook/browser/diff/notebookDiff.css | 11 +- .../browser/diff/notebookTextDiffEditor.ts | 3 +- .../common/model/notebookCellTextModel.ts | 3 +- 5 files changed, 311 insertions(+), 89 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index 3bc00a3b9ce..7be518c7d2b 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -7,7 +7,7 @@ import * as DOM from 'vs/base/browser/dom'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { CellDiffViewModel, MetadataFoldingState } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; +import { CellDiffViewModel, PropertyFoldingState } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; import { CellDiffRenderTemplate, CellDiffViewModelLayoutChangeEvent, DIFF_CELL_MARGIN, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/common'; import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; @@ -77,10 +77,110 @@ const fixedEditorOptions: IEditorOptions = { renderLineHighlight: 'none' }; +class PropertyHeader extends Disposable { + protected _foldingIndicator!: HTMLElement; + protected _statusSpan!: HTMLElement; + + constructor( + readonly cell: CellDiffViewModel, + readonly metadataHeaderContainer: HTMLElement, + readonly notebookEditor: INotebookTextDiffEditor, + readonly accessor: { + updateInfoRendering: () => void; + checkIfModified: (cell: CellDiffViewModel) => boolean; + getFoldingState: (cell: CellDiffViewModel) => PropertyFoldingState; + updateFoldingState: (cell: CellDiffViewModel, newState: PropertyFoldingState) => void; + unChangedLabel: string; + changedLabel: string; + prefix: string; + } + ) { + super(); + } + + buildHeader(): void { + let metadataChanged = this.accessor.checkIfModified(this.cell); + this._foldingIndicator = DOM.append(this.metadataHeaderContainer, DOM.$('.property-folding-indicator')); + DOM.addClass(this._foldingIndicator, this.accessor.prefix); + + + this._updateFoldingIcon(); + const metadataStatus = DOM.append(this.metadataHeaderContainer, DOM.$('div.property-status')); + this._statusSpan = DOM.append(metadataStatus, DOM.$('span')); + + if (metadataChanged) { + this._statusSpan.textContent = this.accessor.changedLabel; + this._statusSpan.style.fontWeight = 'bold'; + } else { + this._statusSpan.textContent = this.accessor.unChangedLabel; + } + + this._register(this.notebookEditor.onMouseUp(e => { + if (!e.event.target) { + return; + } + + const target = e.event.target as HTMLElement; + + if (DOM.hasClass(target, 'codicon-chevron-down') || DOM.hasClass(target, 'codicon-chevron-right')) { + const parent = target.parentElement as HTMLElement; + + if (!parent) { + return; + } + + if (!DOM.hasClass(parent, this.accessor.prefix)) { + return; + } + + if (!DOM.hasClass(parent, 'property-folding-indicator')) { + return; + } + + // folding icon + + const cellViewModel = e.target; + + if (cellViewModel === this.cell) { + const oldFoldingState = this.accessor.getFoldingState(this.cell); + this.accessor.updateFoldingState(this.cell, oldFoldingState === PropertyFoldingState.Expanded ? PropertyFoldingState.Collapsed : PropertyFoldingState.Expanded); + this._updateFoldingIcon(); + this.accessor.updateInfoRendering(); + } + } + + return; + })); + + this._updateFoldingIcon(); + this.accessor.updateInfoRendering(); + } + + private _updateFoldingIcon() { + if (this.accessor.getFoldingState(this.cell) === PropertyFoldingState.Collapsed) { + this._foldingIndicator.innerHTML = renderCodicons('$(chevron-right)'); + } else { + this._foldingIndicator.innerHTML = renderCodicons('$(chevron-down)'); + } + } +} + abstract class AbstractCellRenderer extends Disposable { protected _metadataHeaderContainer!: HTMLElement; + protected _metadataHeader!: PropertyHeader; protected _metadataInfoContainer!: HTMLElement; - protected _metadataStatusSpan!: HTMLElement; + protected _metadataEditorContainer?: HTMLElement; + protected _metadataEditorDisposeStore!: DisposableStore; + protected _metadataEditor?: CodeEditorWidget | DiffEditorWidget; + + protected _outputHeaderContainer!: HTMLElement; + protected _outputHeader!: PropertyHeader; + protected _outputInfoContainer!: HTMLElement; + protected _outputEditorContainer?: HTMLElement; + protected _outputEditorDisposeStore!: DisposableStore; + protected _outputEditor?: CodeEditorWidget | DiffEditorWidget; + + protected _diffEditorContainer!: HTMLElement; protected _diagonalFill?: HTMLElement; protected _layoutInfo!: { @@ -88,12 +188,10 @@ abstract class AbstractCellRenderer extends Disposable { editorMargin: number; metadataStatusHeight: number; metadataHeight: number; + outputStatusHeight: number; + outputHeight: number; bodyMargin: number; }; - protected _foldingIndicator!: HTMLElement; - protected _metadataEditorContainer?: HTMLElement; - protected _metadataEditorDisposeStore!: DisposableStore; - protected _metadataEditor?: CodeEditorWidget | DiffEditorWidget; constructor( readonly notebookEditor: INotebookTextDiffEditor, @@ -112,9 +210,12 @@ abstract class AbstractCellRenderer extends Disposable { editorMargin: 0, metadataHeight: 0, metadataStatusHeight: 25, + outputHeight: 0, + outputStatusHeight: 25, bodyMargin: 32 }; this._metadataEditorDisposeStore = new DisposableStore(); + this._outputEditorDisposeStore = new DisposableStore(); this._register(this._metadataEditorDisposeStore); this.initData(); this.buildBody(templateData.container); @@ -139,65 +240,65 @@ abstract class AbstractCellRenderer extends Disposable { DOM.append(body, this._diffEditorContainer); this._diagonalFill = DOM.append(body, DOM.$('.diagonal-fill')); - // this._diagonalFill.style.display = '0px'; this.styleContainer(this._diffEditorContainer); const sourceContainer = DOM.append(this._diffEditorContainer, DOM.$('.source-container')); this.buildSourceEditor(sourceContainer); this._metadataHeaderContainer = DOM.append(this._diffEditorContainer, DOM.$('.metadata-header-container')); this._metadataInfoContainer = DOM.append(this._diffEditorContainer, DOM.$('.metadata-info-container')); - this.buildMetadataHeader(this._metadataHeaderContainer); - // this.buildMetadataBody(this._metadataInfoContainer); - } - buildMetadataHeader(metadataHeaderContainer: HTMLElement): void { - let metadataChanged = this.cell.type === 'modified' && hash(this.cell.original?.metadata ?? {}) !== hash(this.cell.modified?.metadata ?? {}); - this._foldingIndicator = DOM.append(metadataHeaderContainer, DOM.$('.metadata-folding-indicator')); - - - this._updateFoldingIcon(); - const metadataStatus = DOM.append(metadataHeaderContainer, DOM.$('div.metadata-status')); - this._metadataStatusSpan = DOM.append(metadataStatus, DOM.$('span')); - - if (metadataChanged) { - this._metadataStatusSpan.textContent = 'Metadata changed'; - this._metadataStatusSpan.style.fontWeight = 'bold'; - } else { - this._metadataStatusSpan.textContent = 'Metadata'; - } - - this._register(this.notebookEditor.onMouseUp(e => { - if (!e.event.target) { - return; + this._metadataHeader = new PropertyHeader( + this.cell, + this._metadataHeaderContainer, + this.notebookEditor, + { + updateInfoRendering: this.updateMetadataRendering.bind(this), + checkIfModified: (cell) => { + return cell.type === 'modified' && hash(cell.original?.metadata ?? {}) !== hash(cell.modified?.metadata ?? {}); + }, + getFoldingState: (cell) => { + return cell.metadataFoldingState; + }, + updateFoldingState: (cell, state) => { + cell.metadataFoldingState = state; + }, + unChangedLabel: 'Metadata', + changedLabel: 'Metadata changed', + prefix: 'metadata' } + ); + this._register(this._metadataHeader); + this._metadataHeader.buildHeader(); - const target = e.event.target as HTMLElement; + this._outputHeaderContainer = DOM.append(this._diffEditorContainer, DOM.$('.output-header-container')); + this._outputInfoContainer = DOM.append(this._diffEditorContainer, DOM.$('.output-info-container')); - if (DOM.hasClass(target, 'codicon-chevron-down') || DOM.hasClass(target, 'codicon-chevron-right')) { - const parent = target.parentElement as HTMLElement; - - if (parent && !DOM.hasClass(parent, 'metadata-folding-indicator')) { - return; - } - - // folding icon - - const cellViewModel = e.target; - - if (cellViewModel === this.cell) { - this.cell.foldingState = this.cell.foldingState === MetadataFoldingState.Expanded ? MetadataFoldingState.Collapsed : MetadataFoldingState.Expanded; - this.updateMetadataRendering(); - } + this._outputHeader = new PropertyHeader( + this.cell, + this._outputHeaderContainer, + this.notebookEditor, + { + updateInfoRendering: this.updateOutputRendering.bind(this), + checkIfModified: (cell) => { + return cell.type === 'modified' && hash(cell.original?.outputs ?? []) !== hash(cell.modified?.outputs ?? []); + }, + getFoldingState: (cell) => { + return this.cell.outputFoldingState; + }, + updateFoldingState: (cell, state) => { + cell.outputFoldingState = state; + }, + unChangedLabel: 'Outputs', + changedLabel: 'Outputs changed', + prefix: 'output' } - - return; - })); - - this.updateMetadataRendering(); + ); + this._register(this._outputHeader); + this._outputHeader.buildHeader(); } updateMetadataRendering() { - if (this.cell.foldingState === MetadataFoldingState.Expanded) { + if (this.cell.metadataFoldingState === PropertyFoldingState.Expanded) { // we should expand the metadata editor this._metadataInfoContainer.style.display = 'block'; @@ -216,11 +317,30 @@ abstract class AbstractCellRenderer extends Disposable { this._layoutInfo.metadataHeight = 0; this.layout({}); } - - this._updateFoldingIcon(); } - private _getFormatedJSON(metadata: NotebookCellMetadata, language?: string) { + updateOutputRendering() { + if (this.cell.outputFoldingState === PropertyFoldingState.Expanded) { + this._outputInfoContainer.style.display = 'block'; + + if (!this._outputEditorContainer || !this._outputEditor) { + // create editor + this._outputEditorContainer = DOM.append(this._outputInfoContainer, DOM.$('.output-editor-container')); + this._buildOutputEditor(); + } else { + console.log(this.cell); + this._layoutInfo.outputHeight = this._outputEditor.getContentHeight(); + this.layout({ outputEditor: true }); + } + } else { + this._outputInfoContainer.style.display = 'none'; + this._outputEditorDisposeStore.clear(); + this._layoutInfo.outputHeight = 0; + this.layout({}); + } + } + + private _getFormatedMetadataJSON(metadata: NotebookCellMetadata, language?: string) { const content = JSON.stringify({ language, ...metadata @@ -234,8 +354,8 @@ abstract class AbstractCellRenderer extends Disposable { private _buildMetadataEditor() { if (this.cell.type === 'modified') { - const originalMetadataSource = this._getFormatedJSON(this.cell.original?.metadata || {}, this.cell.original?.language); - const modifiedMetadataSource = this._getFormatedJSON(this.cell.modified?.metadata || {}, this.cell.modified?.language); + const originalMetadataSource = this._getFormatedMetadataJSON(this.cell.original?.metadata || {}, this.cell.original?.language); + const modifiedMetadataSource = this._getFormatedMetadataJSON(this.cell.modified?.metadata || {}, this.cell.modified?.language); if (originalMetadataSource !== modifiedMetadataSource) { this._metadataEditor = this.instantiationService.createInstance(DiffEditorWidget, this._metadataEditorContainer!, { ...fixedDiffEditorOptions @@ -255,7 +375,7 @@ abstract class AbstractCellRenderer extends Disposable { this.layout({ metadataEditor: true }); this._register(this._metadataEditor.onDidContentSizeChange((e) => { - if (e.contentHeightChanged && this.cell.foldingState === MetadataFoldingState.Expanded) { + if (e.contentHeightChanged && this.cell.metadataFoldingState === PropertyFoldingState.Expanded) { this._layoutInfo.metadataHeight = e.contentHeight; this.layout({ metadataEditor: true }); } @@ -274,7 +394,7 @@ abstract class AbstractCellRenderer extends Disposable { }, {}); const mode = this.modeService.create('json'); - const originalMetadataSource = this._getFormatedJSON( + const originalMetadataSource = this._getFormatedMetadataJSON( this.cell.type === 'insert' ? this.cell.modified!.metadata || {} : this.cell.original!.metadata || {}); @@ -285,26 +405,100 @@ abstract class AbstractCellRenderer extends Disposable { this.layout({ metadataEditor: true }); this._register(this._metadataEditor.onDidContentSizeChange((e) => { - if (e.contentHeightChanged && this.cell.foldingState === MetadataFoldingState.Expanded) { + if (e.contentHeightChanged && this.cell.metadataFoldingState === PropertyFoldingState.Expanded) { this._layoutInfo.metadataHeight = e.contentHeight; this.layout({ metadataEditor: true }); } })); } - private _updateFoldingIcon() { - if (this.cell.foldingState === MetadataFoldingState.Collapsed) { - this._foldingIndicator.innerHTML = renderCodicons('$(chevron-right)'); - } else { - this._foldingIndicator.innerHTML = renderCodicons('$(chevron-down)'); + private _getFormatedOutputJSON(outputs: any[]) { + const content = JSON.stringify(outputs); + + const edits = format(content, undefined, {}); + const source = applyEdits(content, edits); + + return source; + } + + private _buildOutputEditor() { + if (this.cell.type === 'modified') { + const originalOutputsSource = this._getFormatedOutputJSON(this.cell.original?.outputs || []); + const modifiedOutputsSource = this._getFormatedOutputJSON(this.cell.modified?.outputs || []); + if (originalOutputsSource !== modifiedOutputsSource) { + this._outputEditor = this.instantiationService.createInstance(DiffEditorWidget, this._outputEditorContainer!, { + ...fixedDiffEditorOptions + }); + + DOM.addClass(this._outputEditorContainer!, 'diff'); + + const mode = this.modeService.create('json'); + const originalModel = this.modelService.createModel(originalOutputsSource, mode, undefined, true); + const modifiedModel = this.modelService.createModel(modifiedOutputsSource, mode, undefined, true); + this._outputEditor.setModel({ + original: originalModel, + modified: modifiedModel + }); + + this._layoutInfo.outputHeight = this._outputEditor.getContentHeight(); + this.layout({ outputEditor: true }); + + this._register(this._outputEditor.onDidContentSizeChange((e) => { + if (e.contentHeightChanged && this.cell.outputFoldingState === PropertyFoldingState.Expanded) { + this._layoutInfo.outputHeight = e.contentHeight; + this.layout({ outputEditor: true }); + } + })); + + return; + } } + + this._outputEditor = this.instantiationService.createInstance(CodeEditorWidget, this._outputEditorContainer!, { + ...fixedEditorOptions, + dimension: { + width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true), + height: 0 + } + }, {}); + + const mode = this.modeService.create('json'); + const originaloutputSource = this._getFormatedOutputJSON( + this.cell.type === 'insert' + ? this.cell.modified!.outputs || [] + : this.cell.original!.outputs || []); + const outputModel = this.modelService.createModel(originaloutputSource, mode, undefined, true); + this._outputEditor.setModel(outputModel); + + this._layoutInfo.outputHeight = this._outputEditor.getContentHeight(); + this.layout({ outputEditor: true }); + + this._register(this._outputEditor.onDidContentSizeChange((e) => { + if (e.contentHeightChanged && this.cell.outputFoldingState === PropertyFoldingState.Expanded) { + this._layoutInfo.outputHeight = e.contentHeight; + this.layout({ outputEditor: true }); + } + })); + } + + protected layoutNotebookCell() { + this.notebookEditor.layoutNotebookCell( + this.cell, + this._layoutInfo.editorHeight + + this._layoutInfo.editorMargin + + this._layoutInfo.metadataHeight + + this._layoutInfo.metadataStatusHeight + + this._layoutInfo.outputHeight + + this._layoutInfo.outputStatusHeight + + this._layoutInfo.bodyMargin + ); } abstract initData(): void; abstract styleContainer(container: HTMLElement): void; abstract buildSourceEditor(sourceContainer: HTMLElement): void; abstract onDidLayoutChange(event: CellDiffViewModelLayoutChangeEvent): void; - abstract layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean }): void; + abstract layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean, outputEditor?: boolean }): void; } export class UnchangedCell extends AbstractCellRenderer { @@ -367,7 +561,7 @@ export class UnchangedCell extends AbstractCellRenderer { } } - layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean }) { + layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean, outputEditor?: boolean }) { if (state.editorHeight || state.outerWidth) { this._editor.layout({ width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true), @@ -382,8 +576,14 @@ export class UnchangedCell extends AbstractCellRenderer { }); } - this.notebookEditor.layoutNotebookCell(this.cell, - this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight + this._layoutInfo.bodyMargin); + if (state.outputEditor || state.outerWidth) { + this._outputEditor?.layout({ + width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true), + height: this._layoutInfo.outputHeight + }); + } + + this.layoutNotebookCell(); } } @@ -446,7 +646,7 @@ export class DeletedCell extends AbstractCellRenderer { this.layout({ outerWidth: true }); } } - layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean }) { + layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean, outputEditor?: boolean }) { if (state.editorHeight || state.outerWidth) { this._editor.layout({ width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false), @@ -461,8 +661,14 @@ export class DeletedCell extends AbstractCellRenderer { }); } - this.notebookEditor.layoutNotebookCell(this.cell, - this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight + this._layoutInfo.bodyMargin); + if (state.outputEditor || state.outerWidth) { + this._outputEditor?.layout({ + width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false), + height: this._layoutInfo.outputHeight + }); + } + + this.layoutNotebookCell(); } } @@ -525,7 +731,7 @@ export class InsertCell extends AbstractCellRenderer { } } - layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean }) { + layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean, outputEditor?: boolean }) { if (state.editorHeight || state.outerWidth) { this._editor.layout({ width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false), @@ -534,18 +740,20 @@ export class InsertCell extends AbstractCellRenderer { } if (state.metadataEditor || state.outerWidth) { - if (this._metadataEditorContainer) { - this._metadataEditorContainer.style.height = `${this._layoutInfo.metadataHeight}px`; - } - this._metadataEditor?.layout({ width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true), height: this._layoutInfo.metadataHeight }); } - this.notebookEditor.layoutNotebookCell(this.cell, - this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight + this._layoutInfo.bodyMargin); + if (state.outputEditor || state.outerWidth) { + this._outputEditor?.layout({ + width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true), + height: this._layoutInfo.outputHeight + }); + } + + this.layoutNotebookCell(); } } @@ -626,7 +834,7 @@ export class ModifiedCell extends AbstractCellRenderer { } } - layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean }) { + layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean, outputEditor?: boolean }) { if (state.editorHeight || state.outerWidth) { this._editorContainer.style.height = `${this._layoutInfo.editorHeight}px`; this._editor!.layout(); @@ -639,8 +847,13 @@ export class ModifiedCell extends AbstractCellRenderer { } } - this.notebookEditor.layoutNotebookCell(this.cell, - this._layoutInfo.editorHeight + this._layoutInfo.editorMargin + this._layoutInfo.metadataHeight + this._layoutInfo.metadataStatusHeight + this._layoutInfo.bodyMargin); + if (state.outputEditor || state.outerWidth) { + if (this._outputEditorContainer) { + this._outputEditorContainer.style.height = `${this._layoutInfo.outputHeight}px`; + this._outputEditor?.layout(); + } + } + this.layoutNotebookCell(); } } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel.ts b/src/vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel.ts index 440356733fe..55e25cee313 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel.ts @@ -11,13 +11,14 @@ import { CellDiffViewModelLayoutChangeEvent, DIFF_CELL_MARGIN } from 'vs/workben import { NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; -export enum MetadataFoldingState { +export enum PropertyFoldingState { Expanded, Collapsed } export class CellDiffViewModel extends Disposable { - public foldingState: MetadataFoldingState; + public metadataFoldingState: PropertyFoldingState; + public outputFoldingState: PropertyFoldingState; private _layoutInfoEmitter = new Emitter(); onDidLayoutChange = this._layoutInfoEmitter.event; @@ -29,7 +30,8 @@ export class CellDiffViewModel extends Disposable { readonly editorEventDispatcher: NotebookDiffEditorEventDispatcher ) { super(); - this.foldingState = MetadataFoldingState.Collapsed; + this.metadataFoldingState = PropertyFoldingState.Collapsed; + this.outputFoldingState = PropertyFoldingState.Collapsed; this._register(this.editorEventDispatcher.onDidChangeLayout(e => { this._layoutInfoEmitter.fire({ outerWidth: e.value.width }); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css index af0b26142a3..dda6833c788 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css @@ -36,6 +36,7 @@ } .notebook-text-diff-editor .cell-body .cell-diff-editor-container .metadata-editor-container.diff, +.notebook-text-diff-editor .cell-body .cell-diff-editor-container .output-editor-container.diff, .notebook-text-diff-editor .cell-body .cell-diff-editor-container .editor-container.diff { /** 100% + diffOverviewWidth */ width: calc(100% + 30px); @@ -63,6 +64,7 @@ width: 50%; } +.notebook-text-diff-editor .cell-diff-editor-container .output-header-container, .notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container { display: flex; height: 24px; @@ -70,17 +72,20 @@ cursor: default; } -.notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .metadata-folding-indicator .codicon { +.notebook-text-diff-editor .cell-diff-editor-container .output-header-container .property-folding-indicator .codicon, +.notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .property-folding-indicator .codicon { visibility: visible; padding: 4px 0 0 10px; cursor: pointer; } -.notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .metadata-status { +.notebook-text-diff-editor .cell-diff-editor-container .output-header-container .property-status, +.notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .property-status { font-size: 12px; } -.notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .metadata-status span { +.notebook-text-diff-editor .cell-diff-editor-container .output-header-container .property-status span, +.notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .property-status span { margin: 0 8px; line-height: 21px; } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index d86af68ee5b..3321a5bb2ca 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -298,7 +298,8 @@ registerThemingParticipant((theme, collector) => { const cellBorderColor = theme.getColor(notebookCellBorder); if (cellBorderColor) { collector.addRule(`.notebook-text-diff-editor .cell-body { border: 1px solid ${cellBorderColor};}`); - collector.addRule(`.notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container { + collector.addRule(`.notebook-text-diff-editor .cell-diff-editor-container .output-header-container, + .notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container { border-top: 1px solid ${cellBorderColor}; }`); } diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts index f995e6d503f..2e85398d330 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts @@ -107,7 +107,8 @@ export class NotebookCellTextModel extends Disposable implements ICell { return this._hash; } - this._hash = hash([hash(this.getValue()), this._metadata]); + // TODO, raw outputs + this._hash = hash([hash(this.getValue()), this._metadata, this._outputs]); // this._hash = hash(this.getValue()); return this._hash; } From cd55420e7e8b2c05682231cb294c197f42879782 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Mon, 24 Aug 2020 18:43:59 -0700 Subject: [PATCH 491/736] Make github-authentication a UI extension again --- extensions/github-authentication/package.json | 5 +++++ .../api/browser/mainThreadAuthentication.ts | 2 +- src/vs/workbench/api/common/extHost.protocol.ts | 2 +- .../api/common/extHostExtensionService.ts | 6 +++++- .../extensions/common/abstractExtensionService.ts | 10 +++++++--- .../extensions/common/extensionHostManager.ts | 14 ++++++++++---- .../services/extensions/common/extensions.ts | 7 ++++++- 7 files changed, 35 insertions(+), 11 deletions(-) diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index c4189e6b7eb..787b4d17497 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -11,6 +11,11 @@ "categories": [ "Other" ], + "extensionKind": [ + "ui", + "workspace", + "web" + ], "activationEvents": [ "*", "onAuthenticationRequest:github" diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts index 7a9e0fc6a64..e4fde65fb10 100644 --- a/src/vs/workbench/api/browser/mainThreadAuthentication.ts +++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts @@ -249,7 +249,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu } $ensureProvider(id: string): Promise { - return this.extensionService.activateByEvent(getAuthenticationProviderActivationEvent(id)); + return this.extensionService.activateByEvent(getAuthenticationProviderActivationEvent(id), true); } $sendDidChangeSessions(id: string, event: modes.AuthenticationSessionsChangeEvent): void { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index e09f81490e8..9f0c4e55065 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1074,7 +1074,7 @@ export type IResolveAuthorityResult = IResolveAuthorityErrorResult | IResolveAut export interface ExtHostExtensionServiceShape { $resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise; $startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise; - $activateByEvent(activationEvent: string): Promise; + $activateByEvent(activationEvent: string, eager?: boolean): Promise; $activate(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise; $setRemoteEnvironment(env: { [key: string]: string | null; }): Promise; $updateRemoteConnectionData(connectionData: IRemoteConnectionData): Promise; diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 6c5dcf97dad..0653b623ba0 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -686,7 +686,11 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme return this._startExtensionHost(); } - public $activateByEvent(activationEvent: string): Promise { + public $activateByEvent(activationEvent: string, eager: boolean = true): Promise { + if (eager) { + return this._activateByEvent(activationEvent, false); + } + return ( this._readyToRunExtensions.wait() .then(_ => this._activateByEvent(activationEvent, false)) diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index 446f74eff84..2bd2b6a3282 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -186,7 +186,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx this._startExtensionHosts(false, Array.from(this._allRequestedActivateEvents.keys())); } - public activateByEvent(activationEvent: string): Promise { + public activateByEvent(activationEvent: string, eager?: boolean): Promise { if (this._installedExtensionsReady.isOpen()) { // Extensions have been scanned and interpreted @@ -205,13 +205,17 @@ export abstract class AbstractExtensionService extends Disposable implements IEx // Record the fact that this activationEvent was requested (in case of a restart) this._allRequestedActivateEvents.add(activationEvent); + if (eager) { + return this._activateByEvent(activationEvent, eager); + } + return this._installedExtensionsReady.wait().then(() => this._activateByEvent(activationEvent)); } } - private _activateByEvent(activationEvent: string): Promise { + private _activateByEvent(activationEvent: string, eager?: boolean): Promise { const result = Promise.all( - this._extensionHostManagers.map(extHostManager => extHostManager.activateByEvent(activationEvent)) + this._extensionHostManagers.map(extHostManager => extHostManager.activateByEvent(activationEvent, eager)) ).then(() => { }); this._onWillActivateByEvent.fire({ event: activationEvent, diff --git a/src/vs/workbench/services/extensions/common/extensionHostManager.ts b/src/vs/workbench/services/extensions/common/extensionHostManager.ts index 484444e968d..e3f7f0c117a 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostManager.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostManager.ts @@ -48,6 +48,7 @@ export class ExtensionHostManager extends Disposable { */ private _proxy: Promise<{ value: ExtHostExtensionServiceShape; } | null> | null; private _resolveAuthorityAttempt: number; + private _hasStarted = false; constructor( extensionHost: IExtensionHost, @@ -65,6 +66,7 @@ export class ExtensionHostManager extends Disposable { this.onDidExit = this._extensionHost.onExit; this._proxy = this._extensionHost.start()!.then( (protocol) => { + this._hasStarted = true; return { value: this._createExtensionHostCustomers(protocol) }; }, (err) => { @@ -217,14 +219,18 @@ export class ExtensionHostManager extends Disposable { return proxy.$activate(extension, reason); } - public activateByEvent(activationEvent: string): Promise { + public activateByEvent(activationEvent: string, eager?: boolean): Promise { + if (eager && !this._hasStarted) { + return Promise.resolve(); + } + if (!this._cachedActivationEvents.has(activationEvent)) { - this._cachedActivationEvents.set(activationEvent, this._activateByEvent(activationEvent)); + this._cachedActivationEvents.set(activationEvent, this._activateByEvent(activationEvent, eager)); } return this._cachedActivationEvents.get(activationEvent)!; } - private async _activateByEvent(activationEvent: string): Promise { + private async _activateByEvent(activationEvent: string, eager?: boolean): Promise { if (!this._proxy) { return; } @@ -234,7 +240,7 @@ export class ExtensionHostManager extends Disposable { // i.e. the extension host could not be started return; } - return proxy.value.$activateByEvent(activationEvent); + return proxy.value.$activateByEvent(activationEvent, eager); } public async getInspectPort(tryEnableInspector: boolean): Promise { diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 652a2603156..f0980b0405c 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -177,8 +177,13 @@ export interface IExtensionService { /** * Send an activation event and activate interested extensions. + * + * Normally, this will queue the activation event if the extension hosts are not ready + * and send it to all of them. If the extension needs to be activated before this, + * the eager flag can be used to ignore extension hosts that aren't yet started. Do not + * use this flag unless necessary. */ - activateByEvent(activationEvent: string): Promise; + activateByEvent(activationEvent: string, eager?: boolean): Promise; /** * An promise that resolves when the installed extensions are registered after From 371a7527ec1fdb753a2f417da5fc3790f79e6c80 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 24 Aug 2020 19:08:02 -0700 Subject: [PATCH 492/736] diff editor content height is the max of two editors. --- src/vs/editor/browser/widget/diffEditorWidget.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index 4925dfcd46c..118198424c0 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -517,6 +517,18 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } })); + this._register(editor.onDidContentSizeChange(e => { + const width = this.originalEditor.getContentWidth() + this.modifiedEditor.getContentWidth() + DiffEditorWidget.ONE_OVERVIEW_WIDTH; + const height = Math.max(this.modifiedEditor.getContentHeight(), this.originalEditor.getContentHeight()); + + this._onDidContentSizeChange.fire({ + contentHeight: height, + contentWidth: width, + contentHeightChanged: e.contentHeightChanged, + contentWidthChanged: e.contentWidthChanged + }); + })); + return editor; } @@ -563,8 +575,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE })); this._register(editor.onDidContentSizeChange(e => { - const width = this.originalEditor.getContentWidth() + this.modifiedEditor.getContentWidth(); - const height = this.modifiedEditor.getContentHeight(); + const width = this.originalEditor.getContentWidth() + this.modifiedEditor.getContentWidth() + DiffEditorWidget.ONE_OVERVIEW_WIDTH; + const height = Math.max(this.modifiedEditor.getContentHeight(), this.originalEditor.getContentHeight()); this._onDidContentSizeChange.fire({ contentHeight: height, From 50a10934dd9f3720677c0fa0c2d3c58e8db0e13e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 24 Aug 2020 19:51:37 -0700 Subject: [PATCH 493/736] Use TS 4.1-nightly for building VS Code Adds explicit `!!` operators for a few functions where we return `x is y` assertion types --- build/package.json | 2 +- build/yarn.lock | 8 ++++---- package.json | 2 +- src/vs/platform/workspace/common/workspace.ts | 8 ++++---- src/vs/workbench/common/editor/editorGroup.ts | 2 +- yarn.lock | 8 ++++---- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/build/package.json b/build/package.json index 7392e4b602c..e185594554b 100644 --- a/build/package.json +++ b/build/package.json @@ -45,7 +45,7 @@ "minimist": "^1.2.3", "request": "^2.85.0", "terser": "4.3.8", - "typescript": "^4.0.1-rc", + "typescript": "^4.1.0-dev.20200824", "vsce": "1.48.0", "vscode-telemetry-extractor": "^1.6.0", "xml2js": "^0.4.17" diff --git a/build/yarn.lock b/build/yarn.lock index d610f9cefa7..01ebd186c4c 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -2535,10 +2535,10 @@ typescript@^3.0.1: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== -typescript@^4.0.1-rc: - version "4.0.1-rc" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.1-rc.tgz#8adc78223eae56fe71d906a5fa90c3543b07a677" - integrity sha512-TCkspT3dSKOykbzS3/WSK7pqU2h1d/lEO6i45Afm5Y3XNAEAo8YXTG3kHOQk/wFq/5uPyO1+X8rb/Q+g7UsxJw== +typescript@^4.1.0-dev.20200824: + version "4.1.0-dev.20200824" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.0-dev.20200824.tgz#34c92d9b6e5124600658c0d4e9b8c125beaf577d" + integrity sha512-hTJfocmebnMKoqRw/xs3bL61z87XXtvOUwYtM7zaCX9mAvnfdo1x1bzQlLZAsvdzRIgAHPJQYbqYHKygWkDw6g== typical@^4.0.0: version "4.0.0" diff --git a/package.json b/package.json index a50ce5cc307..1858ff663b8 100644 --- a/package.json +++ b/package.json @@ -164,7 +164,7 @@ "source-map": "^0.4.4", "style-loader": "^1.0.0", "ts-loader": "^4.4.2", - "typescript": "^4.0.1-rc", + "typescript": "^4.1.0-dev.20200824", "typescript-formatter": "7.1.0", "underscore": "^1.8.2", "vinyl": "^2.0.0", diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index 0f2e82d0e85..c0c34d74f57 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -82,9 +82,9 @@ export interface IWorkspaceFoldersChangeEvent { export namespace IWorkspace { export function isIWorkspace(thing: unknown): thing is IWorkspace { - return thing && typeof thing === 'object' + return !!(thing && typeof thing === 'object' && typeof (thing as IWorkspace).id === 'string' - && Array.isArray((thing as IWorkspace).folders); + && Array.isArray((thing as IWorkspace).folders)); } } @@ -127,10 +127,10 @@ export interface IWorkspaceFolderData { export namespace IWorkspaceFolder { export function isIWorkspaceFolder(thing: unknown): thing is IWorkspaceFolder { - return thing && typeof thing === 'object' + return !!(thing && typeof thing === 'object' && URI.isUri((thing as IWorkspaceFolder).uri) && typeof (thing as IWorkspaceFolder).name === 'string' - && typeof (thing as IWorkspaceFolder).toResource === 'function'; + && typeof (thing as IWorkspaceFolder).toResource === 'function'); } } diff --git a/src/vs/workbench/common/editor/editorGroup.ts b/src/vs/workbench/common/editor/editorGroup.ts index 57e5432ed19..97c2058102e 100644 --- a/src/vs/workbench/common/editor/editorGroup.ts +++ b/src/vs/workbench/common/editor/editorGroup.ts @@ -55,7 +55,7 @@ export interface ISerializedEditorGroup { export function isSerializedEditorGroup(obj?: unknown): obj is ISerializedEditorGroup { const group = obj as ISerializedEditorGroup; - return obj && typeof obj === 'object' && Array.isArray(group.editors) && Array.isArray(group.mru); + return !!(obj && typeof obj === 'object' && Array.isArray(group.editors) && Array.isArray(group.mru)); } export class EditorGroup extends Disposable { diff --git a/yarn.lock b/yarn.lock index b760c375232..e813c24b15d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9369,10 +9369,10 @@ typescript@^2.6.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4" integrity sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q= -typescript@^4.0.1-rc: - version "4.0.1-rc" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.1-rc.tgz#8adc78223eae56fe71d906a5fa90c3543b07a677" - integrity sha512-TCkspT3dSKOykbzS3/WSK7pqU2h1d/lEO6i45Afm5Y3XNAEAo8YXTG3kHOQk/wFq/5uPyO1+X8rb/Q+g7UsxJw== +typescript@^4.1.0-dev.20200824: + version "4.1.0-dev.20200824" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.0-dev.20200824.tgz#34c92d9b6e5124600658c0d4e9b8c125beaf577d" + integrity sha512-hTJfocmebnMKoqRw/xs3bL61z87XXtvOUwYtM7zaCX9mAvnfdo1x1bzQlLZAsvdzRIgAHPJQYbqYHKygWkDw6g== uc.micro@^1.0.1, uc.micro@^1.0.3: version "1.0.3" From 4e96ceff54718ecf4ba0d181293ffe7adfda9aa6 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 24 Aug 2020 20:00:01 -0700 Subject: [PATCH 494/736] Revert "Use TS 4.1-nightly for building VS Code" This reverts commit 50a10934dd9f3720677c0fa0c2d3c58e8db0e13e. This seems to be causing with extension require statements --- build/package.json | 2 +- build/yarn.lock | 8 ++++---- package.json | 2 +- src/vs/platform/workspace/common/workspace.ts | 8 ++++---- src/vs/workbench/common/editor/editorGroup.ts | 2 +- yarn.lock | 8 ++++---- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/build/package.json b/build/package.json index e185594554b..7392e4b602c 100644 --- a/build/package.json +++ b/build/package.json @@ -45,7 +45,7 @@ "minimist": "^1.2.3", "request": "^2.85.0", "terser": "4.3.8", - "typescript": "^4.1.0-dev.20200824", + "typescript": "^4.0.1-rc", "vsce": "1.48.0", "vscode-telemetry-extractor": "^1.6.0", "xml2js": "^0.4.17" diff --git a/build/yarn.lock b/build/yarn.lock index 01ebd186c4c..d610f9cefa7 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -2535,10 +2535,10 @@ typescript@^3.0.1: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== -typescript@^4.1.0-dev.20200824: - version "4.1.0-dev.20200824" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.0-dev.20200824.tgz#34c92d9b6e5124600658c0d4e9b8c125beaf577d" - integrity sha512-hTJfocmebnMKoqRw/xs3bL61z87XXtvOUwYtM7zaCX9mAvnfdo1x1bzQlLZAsvdzRIgAHPJQYbqYHKygWkDw6g== +typescript@^4.0.1-rc: + version "4.0.1-rc" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.1-rc.tgz#8adc78223eae56fe71d906a5fa90c3543b07a677" + integrity sha512-TCkspT3dSKOykbzS3/WSK7pqU2h1d/lEO6i45Afm5Y3XNAEAo8YXTG3kHOQk/wFq/5uPyO1+X8rb/Q+g7UsxJw== typical@^4.0.0: version "4.0.0" diff --git a/package.json b/package.json index 1858ff663b8..a50ce5cc307 100644 --- a/package.json +++ b/package.json @@ -164,7 +164,7 @@ "source-map": "^0.4.4", "style-loader": "^1.0.0", "ts-loader": "^4.4.2", - "typescript": "^4.1.0-dev.20200824", + "typescript": "^4.0.1-rc", "typescript-formatter": "7.1.0", "underscore": "^1.8.2", "vinyl": "^2.0.0", diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index c0c34d74f57..0f2e82d0e85 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -82,9 +82,9 @@ export interface IWorkspaceFoldersChangeEvent { export namespace IWorkspace { export function isIWorkspace(thing: unknown): thing is IWorkspace { - return !!(thing && typeof thing === 'object' + return thing && typeof thing === 'object' && typeof (thing as IWorkspace).id === 'string' - && Array.isArray((thing as IWorkspace).folders)); + && Array.isArray((thing as IWorkspace).folders); } } @@ -127,10 +127,10 @@ export interface IWorkspaceFolderData { export namespace IWorkspaceFolder { export function isIWorkspaceFolder(thing: unknown): thing is IWorkspaceFolder { - return !!(thing && typeof thing === 'object' + return thing && typeof thing === 'object' && URI.isUri((thing as IWorkspaceFolder).uri) && typeof (thing as IWorkspaceFolder).name === 'string' - && typeof (thing as IWorkspaceFolder).toResource === 'function'); + && typeof (thing as IWorkspaceFolder).toResource === 'function'; } } diff --git a/src/vs/workbench/common/editor/editorGroup.ts b/src/vs/workbench/common/editor/editorGroup.ts index 97c2058102e..57e5432ed19 100644 --- a/src/vs/workbench/common/editor/editorGroup.ts +++ b/src/vs/workbench/common/editor/editorGroup.ts @@ -55,7 +55,7 @@ export interface ISerializedEditorGroup { export function isSerializedEditorGroup(obj?: unknown): obj is ISerializedEditorGroup { const group = obj as ISerializedEditorGroup; - return !!(obj && typeof obj === 'object' && Array.isArray(group.editors) && Array.isArray(group.mru)); + return obj && typeof obj === 'object' && Array.isArray(group.editors) && Array.isArray(group.mru); } export class EditorGroup extends Disposable { diff --git a/yarn.lock b/yarn.lock index e813c24b15d..b760c375232 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9369,10 +9369,10 @@ typescript@^2.6.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4" integrity sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q= -typescript@^4.1.0-dev.20200824: - version "4.1.0-dev.20200824" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.0-dev.20200824.tgz#34c92d9b6e5124600658c0d4e9b8c125beaf577d" - integrity sha512-hTJfocmebnMKoqRw/xs3bL61z87XXtvOUwYtM7zaCX9mAvnfdo1x1bzQlLZAsvdzRIgAHPJQYbqYHKygWkDw6g== +typescript@^4.0.1-rc: + version "4.0.1-rc" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.1-rc.tgz#8adc78223eae56fe71d906a5fa90c3543b07a677" + integrity sha512-TCkspT3dSKOykbzS3/WSK7pqU2h1d/lEO6i45Afm5Y3XNAEAo8YXTG3kHOQk/wFq/5uPyO1+X8rb/Q+g7UsxJw== uc.micro@^1.0.1, uc.micro@^1.0.3: version "1.0.3" From 8ba70d8bdc3a10e3143cc4a131f333263bc48eef Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 24 Aug 2020 20:19:58 -0700 Subject: [PATCH 495/736] Revert "Make github-authentication a UI extension again" This reverts commit cd55420e7e8b2c05682231cb294c197f42879782. This change seems to have caused issues activating extensions --- extensions/github-authentication/package.json | 5 ----- .../api/browser/mainThreadAuthentication.ts | 2 +- src/vs/workbench/api/common/extHost.protocol.ts | 2 +- .../api/common/extHostExtensionService.ts | 6 +----- .../extensions/common/abstractExtensionService.ts | 10 +++------- .../extensions/common/extensionHostManager.ts | 14 ++++---------- .../services/extensions/common/extensions.ts | 7 +------ 7 files changed, 11 insertions(+), 35 deletions(-) diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index 787b4d17497..c4189e6b7eb 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -11,11 +11,6 @@ "categories": [ "Other" ], - "extensionKind": [ - "ui", - "workspace", - "web" - ], "activationEvents": [ "*", "onAuthenticationRequest:github" diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts index e4fde65fb10..7a9e0fc6a64 100644 --- a/src/vs/workbench/api/browser/mainThreadAuthentication.ts +++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts @@ -249,7 +249,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu } $ensureProvider(id: string): Promise { - return this.extensionService.activateByEvent(getAuthenticationProviderActivationEvent(id), true); + return this.extensionService.activateByEvent(getAuthenticationProviderActivationEvent(id)); } $sendDidChangeSessions(id: string, event: modes.AuthenticationSessionsChangeEvent): void { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 9f0c4e55065..e09f81490e8 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1074,7 +1074,7 @@ export type IResolveAuthorityResult = IResolveAuthorityErrorResult | IResolveAut export interface ExtHostExtensionServiceShape { $resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise; $startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise; - $activateByEvent(activationEvent: string, eager?: boolean): Promise; + $activateByEvent(activationEvent: string): Promise; $activate(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise; $setRemoteEnvironment(env: { [key: string]: string | null; }): Promise; $updateRemoteConnectionData(connectionData: IRemoteConnectionData): Promise; diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 0653b623ba0..6c5dcf97dad 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -686,11 +686,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme return this._startExtensionHost(); } - public $activateByEvent(activationEvent: string, eager: boolean = true): Promise { - if (eager) { - return this._activateByEvent(activationEvent, false); - } - + public $activateByEvent(activationEvent: string): Promise { return ( this._readyToRunExtensions.wait() .then(_ => this._activateByEvent(activationEvent, false)) diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index 2bd2b6a3282..446f74eff84 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -186,7 +186,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx this._startExtensionHosts(false, Array.from(this._allRequestedActivateEvents.keys())); } - public activateByEvent(activationEvent: string, eager?: boolean): Promise { + public activateByEvent(activationEvent: string): Promise { if (this._installedExtensionsReady.isOpen()) { // Extensions have been scanned and interpreted @@ -205,17 +205,13 @@ export abstract class AbstractExtensionService extends Disposable implements IEx // Record the fact that this activationEvent was requested (in case of a restart) this._allRequestedActivateEvents.add(activationEvent); - if (eager) { - return this._activateByEvent(activationEvent, eager); - } - return this._installedExtensionsReady.wait().then(() => this._activateByEvent(activationEvent)); } } - private _activateByEvent(activationEvent: string, eager?: boolean): Promise { + private _activateByEvent(activationEvent: string): Promise { const result = Promise.all( - this._extensionHostManagers.map(extHostManager => extHostManager.activateByEvent(activationEvent, eager)) + this._extensionHostManagers.map(extHostManager => extHostManager.activateByEvent(activationEvent)) ).then(() => { }); this._onWillActivateByEvent.fire({ event: activationEvent, diff --git a/src/vs/workbench/services/extensions/common/extensionHostManager.ts b/src/vs/workbench/services/extensions/common/extensionHostManager.ts index e3f7f0c117a..484444e968d 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostManager.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostManager.ts @@ -48,7 +48,6 @@ export class ExtensionHostManager extends Disposable { */ private _proxy: Promise<{ value: ExtHostExtensionServiceShape; } | null> | null; private _resolveAuthorityAttempt: number; - private _hasStarted = false; constructor( extensionHost: IExtensionHost, @@ -66,7 +65,6 @@ export class ExtensionHostManager extends Disposable { this.onDidExit = this._extensionHost.onExit; this._proxy = this._extensionHost.start()!.then( (protocol) => { - this._hasStarted = true; return { value: this._createExtensionHostCustomers(protocol) }; }, (err) => { @@ -219,18 +217,14 @@ export class ExtensionHostManager extends Disposable { return proxy.$activate(extension, reason); } - public activateByEvent(activationEvent: string, eager?: boolean): Promise { - if (eager && !this._hasStarted) { - return Promise.resolve(); - } - + public activateByEvent(activationEvent: string): Promise { if (!this._cachedActivationEvents.has(activationEvent)) { - this._cachedActivationEvents.set(activationEvent, this._activateByEvent(activationEvent, eager)); + this._cachedActivationEvents.set(activationEvent, this._activateByEvent(activationEvent)); } return this._cachedActivationEvents.get(activationEvent)!; } - private async _activateByEvent(activationEvent: string, eager?: boolean): Promise { + private async _activateByEvent(activationEvent: string): Promise { if (!this._proxy) { return; } @@ -240,7 +234,7 @@ export class ExtensionHostManager extends Disposable { // i.e. the extension host could not be started return; } - return proxy.value.$activateByEvent(activationEvent, eager); + return proxy.value.$activateByEvent(activationEvent); } public async getInspectPort(tryEnableInspector: boolean): Promise { diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index f0980b0405c..652a2603156 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -177,13 +177,8 @@ export interface IExtensionService { /** * Send an activation event and activate interested extensions. - * - * Normally, this will queue the activation event if the extension hosts are not ready - * and send it to all of them. If the extension needs to be activated before this, - * the eager flag can be used to ignore extension hosts that aren't yet started. Do not - * use this flag unless necessary. */ - activateByEvent(activationEvent: string, eager?: boolean): Promise; + activateByEvent(activationEvent: string): Promise; /** * An promise that resolves when the installed extensions are registered after From 1efd647d372546b170771b59aaf9d6dce03ef19b Mon Sep 17 00:00:00 2001 From: Jonatan Ivanov Date: Mon, 24 Aug 2020 21:36:11 -0700 Subject: [PATCH 496/736] Adding support for .jenkinsfile extension and Jenkinsfile.* filename pattern So that it will be consistent with the support of Dockerfile. The file is using tabs AND spaces for indentation, this commit follows the formatting rules defined in .editorconfig (2 spaces). --- extensions/groovy/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/groovy/package.json b/extensions/groovy/package.json index 55da6a97fc8..ab2ef0da7ba 100644 --- a/extensions/groovy/package.json +++ b/extensions/groovy/package.json @@ -13,8 +13,8 @@ "languages": [{ "id": "groovy", "aliases": ["Groovy", "groovy"], - "extensions": [".groovy", ".gvy", ".gradle"], - "filenames": [ "Jenkinsfile" ], + "extensions": [".groovy", ".gvy", ".gradle", ".jenkinsfile"], + "filenamePatterns": ["Jenkinsfile.*"], "firstLine": "^#!.*\\bgroovy\\b", "configuration": "./language-configuration.json" }], From 302e86a1324939d74d7652146b3c3d1039bbbe30 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 25 Aug 2020 09:28:18 +0200 Subject: [PATCH 497/736] sandbox - some tweaks around loading modules lazy if possible(semver-umd) --- src/vs/code/electron-browser/workbench/workbench.js | 4 +--- src/vs/workbench/contrib/update/browser/update.ts | 4 ++-- .../extensionManagement/common/webExtensionsScannerService.ts | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index 46340a94e38..619669fafca 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -62,9 +62,7 @@ bootstrapWindow.load([ { removeDeveloperKeybindingsAfterLoad: true, canModifyDOM: function (windowConfig) { - if (!bootstrapWindow.globals().context.sandbox) { - showPartsSplash(windowConfig); // TODO@sandbox non-sandboxed only - } + showPartsSplash(windowConfig); }, beforeLoaderConfig: function (windowConfig, loaderConfig) { loaderConfig.recordStats = true; diff --git a/src/vs/workbench/contrib/update/browser/update.ts b/src/vs/workbench/contrib/update/browser/update.ts index d42000cbbee..8c45b22c10a 100644 --- a/src/vs/workbench/contrib/update/browser/update.ts +++ b/src/vs/workbench/contrib/update/browser/update.ts @@ -14,7 +14,6 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IUpdateService, State as UpdateState, StateType, IUpdate } from 'vs/platform/update/common/update'; -import * as semver from 'semver-umd'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -131,7 +130,7 @@ export class ProductContribution implements IWorkbenchContribution { @IHostService hostService: IHostService, @IProductService productService: IProductService ) { - hostService.hadLastFocus().then(hadLastFocus => { + hostService.hadLastFocus().then(async hadLastFocus => { if (!hadLastFocus) { return; } @@ -160,6 +159,7 @@ export class ProductContribution implements IWorkbenchContribution { } // should we show the new license? + const semver = await import('semver-umd'); if (productService.licenseUrl && lastVersion && semver.satisfies(lastVersion, '<1.0.0') && semver.satisfies(productService.version, '>=1.0.0')) { notificationService.info(nls.localize('licenseChanged', "Our license terms have changed, please click [here]({0}) to go through them.", productService.licenseUrl)); } diff --git a/src/vs/workbench/services/extensionManagement/common/webExtensionsScannerService.ts b/src/vs/workbench/services/extensionManagement/common/webExtensionsScannerService.ts index 2faf33d9c22..2e8d7fa49b0 100644 --- a/src/vs/workbench/services/extensionManagement/common/webExtensionsScannerService.ts +++ b/src/vs/workbench/services/extensionManagement/common/webExtensionsScannerService.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as semver from 'semver-umd'; import { IBuiltinExtensionsScannerService, IScannedExtension, ExtensionType, IExtensionIdentifier, ITranslatedScannedExtension } from 'vs/platform/extensions/common/extensions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; @@ -225,6 +224,7 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten } private async scanUserExtensions(): Promise { + const semver = await import('semver-umd'); let userExtensions = await this.readUserExtensions(); const byExtension: IUserExtension[][] = groupByExtension(userExtensions, e => e.identifier); userExtensions = byExtension.map(p => p.sort((a, b) => semver.rcompare(a.version, b.version))[0]); From 4ccab0deda29628076ce1bbb17e9fef239337f21 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 25 Aug 2020 09:46:01 +0200 Subject: [PATCH 498/736] Fix #105336 --- src/vs/platform/userDataSync/common/keybindingsSync.ts | 2 +- src/vs/platform/userDataSync/test/common/userDataSyncClient.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/userDataSync/common/keybindingsSync.ts b/src/vs/platform/userDataSync/common/keybindingsSync.ts index 412447685d1..3d9816563da 100644 --- a/src/vs/platform/userDataSync/common/keybindingsSync.ts +++ b/src/vs/platform/userDataSync/common/keybindingsSync.ts @@ -38,7 +38,7 @@ interface IKeybindingsResourcePreview extends IFileResourcePreview { export function getKeybindingsContentFromSyncContent(syncContent: string, platformSpecific: boolean): string | null { const parsed = JSON.parse(syncContent); - if (platformSpecific) { + if (!platformSpecific) { return isUndefined(parsed.all) ? null : parsed.all; } switch (OS) { diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts index 767fbbf6cb5..aa427813097 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts @@ -6,7 +6,7 @@ import { IRequestService } from 'vs/platform/request/common/request'; import { IRequestOptions, IRequestContext, IHeaders } from 'vs/base/parts/request/common/request'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IUserData, IUserDataManifest, ALL_SYNC_RESOURCES, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSyncUtilService, IUserDataSyncResourceEnablementService, IUserDataSyncService, getDefaultIgnoredSettings, IUserDataSyncBackupStoreService, SyncResource, ServerResource, IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserData, IUserDataManifest, ALL_SYNC_RESOURCES, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSyncUtilService, IUserDataSyncResourceEnablementService, IUserDataSyncService, getDefaultIgnoredSettings, IUserDataSyncBackupStoreService, SyncResource, ServerResource, IUserDataSyncStoreManagementService, registerConfiguration } from 'vs/platform/userDataSync/common/userDataSync'; import { bufferToStream, VSBuffer } from 'vs/base/common/buffer'; import { generateUuid } from 'vs/base/common/uuid'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; @@ -49,6 +49,7 @@ export class UserDataSyncClient extends Disposable { } async setUp(empty: boolean = false): Promise { + registerConfiguration(); const userRoamingDataHome = URI.file('userdata').with({ scheme: Schemas.inMemory }); const userDataSyncHome = joinPath(userRoamingDataHome, '.sync'); const environmentService = this.instantiationService.stub(IEnvironmentService, >{ From e9495c1e60541c3c78832a963043769168927ede Mon Sep 17 00:00:00 2001 From: annkamsk Date: Tue, 25 Aug 2020 10:54:31 +0200 Subject: [PATCH 499/736] Use DOM API for creating HTML elements instead of string concatenation --- src/vs/base/browser/dom.ts | 12 + .../issue/issueReporterMain.ts | 221 ++++++++++-------- 2 files changed, 137 insertions(+), 96 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 29490d6fc1e..0e254009357 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -1009,6 +1009,18 @@ export function prepend(parent: HTMLElement, child: T): T { const SELECTOR_REGEX = /([\w\-]+)?(#([\w\-]+))?((\.([\w\-]+))*)/; +export function reset(parent: HTMLElement, ...children: Array) { + parent.innerText = ''; + coalesce(children) + .forEach(child => { + if (child instanceof Node) { + parent.appendChild(child); + } else { + parent.appendChild(document.createTextNode(child as string)); + } + }); +} + export enum Namespace { HTML = 'http://www.w3.org/1999/xhtml', SVG = 'http://www.w3.org/2000/svg' diff --git a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts index fd820cf9f58..cc33ad9c397 100644 --- a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts @@ -8,7 +8,7 @@ import 'vs/base/browser/ui/codicons/codiconStyles'; // make sure codicon css is import { ElectronService, IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { ipcRenderer, process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { applyZoom, zoomIn, zoomOut } from 'vs/platform/windows/electron-sandbox/window'; -import { $, windowOpenNoOpener, addClass } from 'vs/base/browser/dom'; +import { $, reset, windowOpenNoOpener, addClass } from 'vs/base/browser/dom'; import { Button } from 'vs/base/browser/ui/button/button'; import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel'; import * as collections from 'vs/base/common/collections'; @@ -277,33 +277,25 @@ export class IssueReporter extends Disposable { } private updateSettingsSearchDetails(data: ISettingsSearchIssueReporterData): void { - const target = document.querySelector('.block-settingsSearchResults .block-info'); + const target = document.querySelector('.block-settingsSearchResults .block-info'); if (target) { - const details = ` -
    -
    Query: "${data.query}"
    -
    Literal match count: ${data.filterResultCount}
    -
    - `; + const queryDiv = $('div', undefined, `Query: "${data.query}"` as string); + const countDiv = $('div', undefined, `Literal match count: ${data.filterResultCount}` as string); + const detailsDiv = $('.block-settingsSearchResults-details', undefined, queryDiv, countDiv); - let table = ` - - Setting - Extension - Score - `; - - data.actualSearchResults - .forEach(setting => { - table += ` - - ${setting.key} - ${setting.extensionId} - ${String(setting.score).slice(0, 5)} - `; - }); - - target.innerHTML = `${details}${table}
    `; + const table = $('table', undefined, + $('tr', undefined, + $('th', undefined, 'Setting'), + $('th', undefined, 'Extension'), + $('th', undefined, 'Score'), + ), + ...data.actualSearchResults.map(setting => $('tr', undefined, + $('td', undefined, setting.key), + $('td', undefined, setting.extensionId), + $('td', undefined, String(setting.score).slice(0, 5)), + )) + ); + reset(target, detailsDiv, table); } } @@ -654,9 +646,9 @@ export class IssueReporter extends Disposable { issueState.appendChild(issueIcon); issueState.appendChild(issueStateLabel); - item = $('div.issue', {}, issueState, link); + item = $('div.issue', undefined, issueState, link); } else { - item = $('div.issue', {}, link); + item = $('div.issue', undefined, link); } issues.appendChild(item); @@ -672,19 +664,19 @@ export class IssueReporter extends Disposable { } private setUpTypes(): void { - const makeOption = (issueType: IssueType, description: string) => ``; + const makeOption = (issueType: IssueType, description: string) => $('option', { 'value': issueType.valueOf() }, escape(description)); const typeSelect = this.getElementById('issue-type')! as HTMLSelectElement; const { issueType } = this.issueReporterModel.getData(); if (issueType === IssueType.SettingsSearchIssue) { - typeSelect.innerHTML = makeOption(IssueType.SettingsSearchIssue, localize('settingsSearchIssue', "Settings Search Issue")); + reset(typeSelect, makeOption(IssueType.SettingsSearchIssue, localize('settingsSearchIssue', "Settings Search Issue"))); typeSelect.disabled = true; } else { - typeSelect.innerHTML = [ + reset(typeSelect, makeOption(IssueType.Bug, localize('bugReporter', "Bug Report")), makeOption(IssueType.FeatureRequest, localize('featureRequest', "Feature Request")), makeOption(IssueType.PerformanceIssue, localize('performanceIssue', "Performance Issue")) - ].join('\n'); + ); } typeSelect.value = issueType.toString(); @@ -774,9 +766,8 @@ export class IssueReporter extends Disposable { } else { show(extensionsBlock); } - - descriptionTitle.innerHTML = `${localize('stepsToReproduce', "Steps to Reproduce")} *`; - descriptionSubtitle.innerHTML = localize('bugDescription', "Share the steps needed to reliably reproduce the problem. Please include actual and expected results. We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub."); + reset(descriptionTitle, localize('stepsToReproduce', "Steps to Reproduce"), $('span.required-input', undefined, '*')); + reset(descriptionSubtitle, localize('bugDescription', "Share the steps needed to reliably reproduce the problem. Please include actual and expected results. We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub.")); } else if (issueType === IssueType.PerformanceIssue) { show(blockContainer); show(systemBlock); @@ -790,11 +781,11 @@ export class IssueReporter extends Disposable { show(extensionsBlock); } - descriptionTitle.innerHTML = `${localize('stepsToReproduce', "Steps to Reproduce")} *`; - descriptionSubtitle.innerHTML = localize('performanceIssueDesciption', "When did this performance issue happen? Does it occur on startup or after a specific series of actions? We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub."); + reset(descriptionTitle, localize('stepsToReproduce', "Steps to Reproduce"), $('span.required-input', undefined, '*')); + reset(descriptionSubtitle, localize('performanceIssueDesciption', "When did this performance issue happen? Does it occur on startup or after a specific series of actions? We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub.")); } else if (issueType === IssueType.FeatureRequest) { - descriptionTitle.innerHTML = `${localize('description', "Description")} *`; - descriptionSubtitle.innerHTML = localize('featureRequestDescription', "Please describe the feature you would like to see. We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub."); + reset(descriptionTitle, localize('description', "Description"), $('span.required-input', undefined, '*')); + reset(descriptionSubtitle, localize('featureRequestDescription', "Please describe the feature you would like to see. We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub.")); show(problemSource); if (fileOnExtension) { @@ -805,8 +796,8 @@ export class IssueReporter extends Disposable { show(searchedExtensionsBlock); show(settingsSearchResultsBlock); - descriptionTitle.innerHTML = `${localize('expectedResults', "Expected Results")} *`; - descriptionSubtitle.innerHTML = localize('settingsSearchResultsDescription', "Please list the results that you were expecting to see when you searched with this query. We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub."); + reset(descriptionTitle, localize('expectedResults', "Expected Results"), $('span.required-input', undefined, '*')); + reset(descriptionSubtitle, localize('settingsSearchResultsDescription', "Please list the results that you were expecting to see when you searched with this query. We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub.")); } } @@ -928,42 +919,82 @@ export class IssueReporter extends Disposable { } private updateSystemInfo(state: IssueReporterModelData) { - const target = document.querySelector('.block-system .block-info'); + const target = document.querySelector('.block-system .block-info'); + if (target) { const systemInfo = state.systemInfo!; - let renderedData = ` - - - - - - - - -
    CPUs${systemInfo.cpus}
    GPU Status${Object.keys(systemInfo.gpuStatus).map(key => `${key}: ${systemInfo.gpuStatus[key]}`).join('
    ')}
    Load (avg)${systemInfo.load}
    Memory (System)${systemInfo.memory}
    Process Argv${systemInfo.processArgs}
    Screen Reader${systemInfo.screenReader}
    VM${systemInfo.vmHint}
    `; + const renderedDataTable = $('table', undefined, + $('tr', undefined, + $('td', undefined, 'CPUs'), + $('td', undefined, systemInfo.cpus || ''), + ), + $('tr', undefined, + $('td', undefined, 'GPU Status' as string), + $('td', undefined, Object.keys(systemInfo.gpuStatus).map(key => `${key}: ${systemInfo.gpuStatus[key]}`).join('\n')), + ), + $('tr', undefined, + $('td', undefined, 'Load (avg)' as string), + $('td', undefined, systemInfo.load || ''), + ), + $('tr', undefined, + $('td', undefined, 'Memory (System)' as string), + $('td', undefined, systemInfo.memory), + ), + $('tr', undefined, + $('td', undefined, 'Process Argv' as string), + $('td', undefined, systemInfo.processArgs), + ), + $('tr', undefined, + $('td', undefined, 'Screen Reader' as string), + $('td', undefined, systemInfo.screenReader), + ), + $('tr', undefined, + $('td', undefined, 'VM'), + $('td', undefined, systemInfo.vmHint), + ), + ); + reset(target, renderedDataTable); systemInfo.remoteData.forEach(remote => { + target.appendChild($('hr')); if (isRemoteDiagnosticError(remote)) { - renderedData += ` -
    - - - -
    Remote${remote.hostName}
    ${remote.errorMessage}
    `; + const remoteDataTable = $('table', undefined, + $('tr', undefined, + $('td', undefined, 'Remote'), + $('td', undefined, remote.hostName) + ), + $('tr', undefined, + $('td', undefined, ''), + $('td', undefined, remote.errorMessage) + ) + ); + target.appendChild(remoteDataTable); } else { - renderedData += ` -
    - - - - - - -
    Remote${remote.hostName}
    OS${remote.machineInfo.os}
    CPUs${remote.machineInfo.cpus}
    Memory (System)${remote.machineInfo.memory}
    VM${remote.machineInfo.vmHint}
    `; + const remoteDataTable = $('table', undefined, + $('tr', undefined, + $('td', undefined, 'Remote'), + $('td', undefined, remote.hostName) + ), + $('tr', undefined, + $('td', undefined, 'OS'), + $('td', undefined, remote.machineInfo.os) + ), + $('tr', undefined, + $('td', undefined, 'CPUs'), + $('td', undefined, remote.machineInfo.cpus || '') + ), + $('tr', undefined, + $('td', undefined, 'Memory (System)' as string), + $('td', undefined, remote.machineInfo.memory) + ), + $('tr', undefined, + $('td', undefined, 'VM'), + $('td', undefined, remote.machineInfo.vmHint) + ), + ); + target.appendChild(remoteDataTable); } }); - - target.innerHTML = renderedData; } } @@ -995,15 +1026,18 @@ export class IssueReporter extends Disposable { return 0; }); - const makeOption = (extension: IOption, selectedExtension?: IssueReporterExtensionData) => { + const makeOption = (extension: IOption, selectedExtension?: IssueReporterExtensionData): HTMLOptionElement => { const selected = selectedExtension && extension.id === selectedExtension.id; - return ``; + return $('option', { + 'value': extension.id, + 'selected': selected || '' + }, extension.name); }; const extensionsSelector = this.getElementById('extension-selector'); if (extensionsSelector) { const { selectedExtension } = this.issueReporterModel.getData(); - extensionsSelector.innerHTML = '' + extensionOptions.map(extension => makeOption(extension, selectedExtension)).join('\n'); + reset(extensionsSelector, $('option'), ...extensionOptions.map(extension => makeOption(extension, selectedExtension))); this.addEventListener('extension-selector', 'change', (e: Event) => { const selectedExtensionId = (e.target).value; @@ -1071,9 +1105,9 @@ export class IssueReporter extends Disposable { } private updateProcessInfo(state: IssueReporterModelData) { - const target = document.querySelector('.block-process .block-info'); + const target = document.querySelector('.block-process .block-info') as HTMLElement; if (target) { - target.innerHTML = `${state.processInfo}`; + reset(target, $('code', undefined, state.processInfo)); } } @@ -1085,7 +1119,7 @@ export class IssueReporter extends Disposable { const target = document.querySelector('.block-extensions .block-info'); if (target) { if (this.configuration.disableExtensions) { - target.innerHTML = localize('disabledExtensions', "Extensions are disabled"); + reset(target, localize('disabledExtensions', "Extensions are disabled")); return; } @@ -1097,8 +1131,7 @@ export class IssueReporter extends Disposable { return; } - const table = this.getExtensionTableHtml(extensions); - target.innerHTML = `${table}
    ${themeExclusionStr}`; + reset(target, this.getExtensionTableHtml(extensions), document.createTextNode(themeExclusionStr)); } } @@ -1111,28 +1144,24 @@ export class IssueReporter extends Disposable { } const table = this.getExtensionTableHtml(extensions); - target.innerHTML = `${table}
    `; + target.innerText = ''; + target.appendChild(table); } } - private getExtensionTableHtml(extensions: IssueReporterExtensionData[]): string { - let table = ` - - Extension - Author (truncated) - Version - `; - - table += extensions.map(extension => { - return ` - - ${extension.name} - ${extension.publisher.substr(0, 3)} - ${extension.version} - `; - }).join(''); - - return table; + private getExtensionTableHtml(extensions: IssueReporterExtensionData[]): HTMLTableElement { + return $('table', undefined, + $('tr', undefined, + $('th', undefined, 'Extension'), + $('th', undefined, 'Author (truncated)' as string), + $('th', undefined, 'Version'), + ), + ...extensions.map(extension => $('tr', undefined, + $('td', undefined, extension.name), + $('td', undefined, extension.publisher.substr(0, 3)), + $('td', undefined, extension.version), + )) + ); } private openLink(event: MouseEvent): void { From 3773aa73a34eab247cb7ac175073e85b1b411d5b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 25 Aug 2020 11:04:43 +0200 Subject: [PATCH 500/736] fix https://github.com/microsoft/vscode/issues/103745 --- src/vs/editor/contrib/format/formatActions.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index 350aeef860e..bedb20cdd97 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -267,14 +267,16 @@ class FormatSelectionAction extends EditorAction { } const instaService = accessor.get(IInstantiationService); const model = editor.getModel(); - let range: Range = editor.getSelection(); - if (range.isEmpty()) { - range = new Range(range.startLineNumber, 1, range.startLineNumber, model.getLineMaxColumn(range.startLineNumber)); - } + + const ranges = editor.getSelections().map(range => { + return range.isEmpty() + ? new Range(range.startLineNumber, 1, range.startLineNumber, model.getLineMaxColumn(range.startLineNumber)) + : range; + }); const progressService = accessor.get(IEditorProgressService); await progressService.showWhile( - instaService.invokeFunction(formatDocumentRangesWithSelectedProvider, editor, range, FormattingMode.Explicit, Progress.None, CancellationToken.None), + instaService.invokeFunction(formatDocumentRangesWithSelectedProvider, editor, ranges, FormattingMode.Explicit, Progress.None, CancellationToken.None), 250 ); } From ac58587ea4f1b3ec493d53f9993f8d5587f8b264 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 25 Aug 2020 12:32:47 +0200 Subject: [PATCH 501/736] Fix #105291 From 3697079bceb6e1f88d171a9abf83b3b76bc314ab Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 25 Aug 2020 13:02:58 +0200 Subject: [PATCH 502/736] fix #105235 --- .../contrib/extensions/browser/workspaceRecommendations.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts index 1175c59a129..889ba988957 100644 --- a/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts @@ -71,7 +71,7 @@ export class WorkspaceRecommendations extends ExtensionRecommendations { const { invalidRecommendations, message } = await this.validateExtensions(extensionsConfigBySource.map(({ contents }) => contents)); if (invalidRecommendations.length) { - this.notificationService.warn(`The below ${invalidRecommendations.length} extension(s) in workspace recommendations have issues:\n${message}`); + this.notificationService.warn(`The ${invalidRecommendations.length} extension(s) below, in workspace recommendations have issues:\n${message}`); } this._ignoredRecommendations = []; From fe83bef4e34aafbb83b374c0c52b4fe9d5c272cd Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 25 Aug 2020 13:10:43 +0200 Subject: [PATCH 503/736] Fix #105291 --- .../browser/configurationService.ts | 11 +++-- .../configurationService.test.ts | 42 +++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 747f5506106..74518d7db33 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -73,6 +73,13 @@ export class WorkspaceService extends Disposable implements IConfigurationServic ) { super(); + const configurationRegistry = Registry.as(Extensions.Configuration); + // register defaults before creating default configuration model + // so that the model is not required to be updated after registering + if (environmentService.options?.configurationDefaults) { + configurationRegistry.registerDefaultConfigurations([environmentService.options.configurationDefaults]); + } + this.completeWorkspaceBarrier = new Barrier(); this.defaultConfiguration = new DefaultConfigurationModel(); this.configurationCache = configurationCache; @@ -94,10 +101,6 @@ export class WorkspaceService extends Disposable implements IConfigurationServic }); })); - const configurationRegistry = Registry.as(Extensions.Configuration); - if (environmentService.options?.configurationDefaults) { - configurationRegistry.registerDefaultConfigurations([environmentService.options.configurationDefaults]); - } this._register(configurationRegistry.onDidSchemaChange(e => this.registerConfigurationSchemas())); this._register(configurationRegistry.onDidUpdateConfiguration(configurationProperties => this.onDefaultConfigurationChanged(configurationProperties))); 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 5091446162d..6d2f43ace73 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 @@ -39,6 +39,7 @@ import { FileService } from 'vs/platform/files/common/fileService'; import { NullLogService } from 'vs/platform/log/common/log'; import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { ConfigurationCache } from 'vs/workbench/services/configuration/node/configurationCache'; +import { ConfigurationCache as BrowserConfigurationCache } from 'vs/workbench/services/configuration/browser/configurationCache'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { IConfigurationCache } from 'vs/workbench/services/configuration/common/configuration'; import { SignService } from 'vs/platform/sign/browser/signService'; @@ -50,6 +51,7 @@ import { timeout } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; import { DisposableStore } from 'vs/base/common/lifecycle'; import product from 'vs/platform/product/common/product'; +import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; class TestEnvironmentService extends NativeWorkbenchEnvironmentService { @@ -2059,6 +2061,46 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { }); +suite('ConfigurationService - Configuration Defaults', () => { + + const disposableStore: DisposableStore = new DisposableStore(); + + suiteSetup(() => { + Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ + 'id': '_test', + 'type': 'object', + 'properties': { + 'configurationService.defaultOverridesSetting': { + 'type': 'string', + 'default': 'isSet', + }, + } + }); + }); + + teardown(() => { + disposableStore.clear(); + }); + + test('when default value is not overriden', () => { + const testObject = createConfiurationService({}); + assert.deepEqual(testObject.getValue('configurationService.defaultOverridesSetting'), 'isSet'); + }); + + test('when default value is overriden', () => { + const testObject = createConfiurationService({ 'configurationService.defaultOverridesSetting': 'overriddenValue' }); + assert.deepEqual(testObject.getValue('configurationService.defaultOverridesSetting'), 'overriddenValue'); + }); + + function createConfiurationService(configurationDefaults: Record): IConfigurationService { + const remoteAgentService = (workbenchInstantiationService()).createInstance(RemoteAgentService); + const environmentService = new BrowserWorkbenchEnvironmentService({ logsPath: URI.file(''), workspaceId: '', configurationDefaults }); + const fileService = new FileService(new NullLogService()); + return disposableStore.add(new WorkspaceService({ configurationCache: new BrowserConfigurationCache() }, environmentService, fileService, remoteAgentService)); + } + +}); + function getWorkspaceId(configPath: URI): string { let workspaceConfigPath = configPath.scheme === Schemas.file ? originalFSPath(configPath) : configPath.toString(); if (!isLinux) { From 49ea79bbb7ff8ead7ad5c7fabefbe1a0b0bf4a45 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 25 Aug 2020 13:35:44 +0200 Subject: [PATCH 504/736] make bulk edit service implementation a contrib, https://github.com/microsoft/vscode/issues/105283 --- .../bulkEdit/browser/bulkEditService.ts | 4 ++-- .../{services => contrib}/bulkEdit/browser/bulkFileEdits.ts | 0 .../{services => contrib}/bulkEdit/browser/bulkTextEdits.ts | 0 .../{services => contrib}/bulkEdit/browser/conflicts.ts | 0 .../bulkEdit/browser/{ => preview}/bulkEdit.contribution.ts | 5 ++--- .../contrib/bulkEdit/browser/{ => preview}/bulkEdit.css | 0 .../contrib/bulkEdit/browser/{ => preview}/bulkEditPane.ts | 4 ++-- .../bulkEdit/browser/{ => preview}/bulkEditPreview.ts | 2 +- .../contrib/bulkEdit/browser/{ => preview}/bulkEditTree.ts | 2 +- .../contrib/bulkEdit/test/browser/bulkEditPreview.test.ts | 2 +- src/vs/workbench/test/browser/api/mainThreadEditors.test.ts | 2 +- src/vs/workbench/workbench.common.main.ts | 4 ++-- 12 files changed, 12 insertions(+), 13 deletions(-) rename src/vs/workbench/{services => contrib}/bulkEdit/browser/bulkEditService.ts (97%) rename src/vs/workbench/{services => contrib}/bulkEdit/browser/bulkFileEdits.ts (100%) rename src/vs/workbench/{services => contrib}/bulkEdit/browser/bulkTextEdits.ts (100%) rename src/vs/workbench/{services => contrib}/bulkEdit/browser/conflicts.ts (100%) rename src/vs/workbench/contrib/bulkEdit/browser/{ => preview}/bulkEdit.contribution.ts (99%) rename src/vs/workbench/contrib/bulkEdit/browser/{ => preview}/bulkEdit.css (100%) rename src/vs/workbench/contrib/bulkEdit/browser/{ => preview}/bulkEditPane.ts (99%) rename src/vs/workbench/contrib/bulkEdit/browser/{ => preview}/bulkEditPreview.ts (99%) rename src/vs/workbench/contrib/bulkEdit/browser/{ => preview}/bulkEditTree.ts (99%) diff --git a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts similarity index 97% rename from src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts rename to src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts index ca37d322d6c..70788d8f7e6 100644 --- a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts @@ -14,8 +14,8 @@ import { IProgress, IProgressStep, Progress } from 'vs/platform/progress/common/ import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { BulkTextEdits } from 'vs/workbench/services/bulkEdit/browser/bulkTextEdits'; -import { BulkFileEdits } from 'vs/workbench/services/bulkEdit/browser/bulkFileEdits'; +import { BulkTextEdits } from 'vs/workbench/contrib/bulkEdit/browser/bulkTextEdits'; +import { BulkFileEdits } from 'vs/workbench/contrib/bulkEdit/browser/bulkFileEdits'; import { ResourceMap } from 'vs/base/common/map'; type Edit = WorkspaceFileEdit | WorkspaceTextEdit; diff --git a/src/vs/workbench/services/bulkEdit/browser/bulkFileEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts similarity index 100% rename from src/vs/workbench/services/bulkEdit/browser/bulkFileEdits.ts rename to src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts diff --git a/src/vs/workbench/services/bulkEdit/browser/bulkTextEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts similarity index 100% rename from src/vs/workbench/services/bulkEdit/browser/bulkTextEdits.ts rename to src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts diff --git a/src/vs/workbench/services/bulkEdit/browser/conflicts.ts b/src/vs/workbench/contrib/bulkEdit/browser/conflicts.ts similarity index 100% rename from src/vs/workbench/services/bulkEdit/browser/conflicts.ts rename to src/vs/workbench/contrib/bulkEdit/browser/conflicts.ts diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts similarity index 99% rename from src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts rename to src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts index cfc08528e27..01159340519 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts @@ -9,14 +9,14 @@ import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } fr import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { WorkspaceEdit } from 'vs/editor/common/modes'; -import { BulkEditPane } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPane'; +import { BulkEditPane } from 'vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane'; import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainerLocation, IViewsRegistry, FocusedViewContext, IViewsService } from 'vs/workbench/common/views'; import { localize } from 'vs/nls'; import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { RawContextKey, IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; -import { BulkEditPreviewProvider } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; +import { BulkEditPreviewProvider } from 'vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { WorkbenchListFocusContextKey } from 'vs/platform/list/browser/listService'; @@ -366,4 +366,3 @@ Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews ctorDescriptor: new SyncDescriptor(BulkEditPane), containerIcon: Codicon.lightbulb.classNames, }], container); - diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.css similarity index 100% rename from src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css rename to src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.css diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts similarity index 99% rename from src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts rename to src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts index b0c6afd8861..395454875aa 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts @@ -6,7 +6,7 @@ import 'vs/css!./bulkEdit'; import { WorkbenchAsyncDataTree, IOpenEvent } from 'vs/platform/list/browser/listService'; import { WorkspaceEdit } from 'vs/editor/common/modes'; -import { BulkEditElement, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider, FileElement, TextEditElement, BulkEditAccessibilityProvider, CategoryElementRenderer, BulkEditNaviLabelProvider, CategoryElement, BulkEditSorter } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditTree'; +import { BulkEditElement, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider, FileElement, TextEditElement, BulkEditAccessibilityProvider, CategoryElementRenderer, BulkEditNaviLabelProvider, CategoryElement, BulkEditSorter } from 'vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree'; import { FuzzyScore } from 'vs/base/common/filters'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { registerThemingParticipant, IColorTheme, ICssStyleCollector, IThemeService } from 'vs/platform/theme/common/themeService'; @@ -14,7 +14,7 @@ import { diffInserted, diffRemoved } from 'vs/platform/theme/common/colorRegistr import { localize } from 'vs/nls'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { BulkEditPreviewProvider, BulkFileOperations, BulkFileOperationType } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; +import { BulkEditPreviewProvider, BulkFileOperations, BulkFileOperationType } from 'vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview'; import { ILabelService } from 'vs/platform/label/common/label'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { URI } from 'vs/base/common/uri'; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts similarity index 99% rename from src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts rename to src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts index d6d0d1510d3..592b4872e08 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts @@ -17,7 +17,7 @@ import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiati import { IFileService } from 'vs/platform/files/common/files'; import { Emitter, Event } from 'vs/base/common/event'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; -import { ConflictDetector } from 'vs/workbench/services/bulkEdit/browser/conflicts'; +import { ConflictDetector } from 'vs/workbench/contrib/bulkEdit/browser/conflicts'; import { ResourceMap } from 'vs/base/common/map'; import { localize } from 'vs/nls'; import { extUri } from 'vs/base/common/resources'; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts similarity index 99% rename from src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts rename to src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts index 52a59197356..b9f669022cb 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts @@ -14,7 +14,7 @@ import * as dom from 'vs/base/browser/dom'; import { ITextModel } from 'vs/editor/common/model'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { BulkFileOperations, BulkFileOperation, BulkFileOperationType, BulkTextEdit, BulkCategory } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; +import { BulkFileOperations, BulkFileOperation, BulkFileOperationType, BulkTextEdit, BulkCategory } from 'vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview'; import { FileKind } from 'vs/platform/files/common/files'; import { localize } from 'vs/nls'; import { ILabelService } from 'vs/platform/label/common/label'; diff --git a/src/vs/workbench/contrib/bulkEdit/test/browser/bulkEditPreview.test.ts b/src/vs/workbench/contrib/bulkEdit/test/browser/bulkEditPreview.test.ts index 768bb672eaa..ebc38122e44 100644 --- a/src/vs/workbench/contrib/bulkEdit/test/browser/bulkEditPreview.test.ts +++ b/src/vs/workbench/contrib/bulkEdit/test/browser/bulkEditPreview.test.ts @@ -13,7 +13,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IModelService } from 'vs/editor/common/services/modelService'; import type { WorkspaceEdit } from 'vs/editor/common/modes'; import { URI } from 'vs/base/common/uri'; -import { BulkFileOperations } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; +import { BulkFileOperations } from 'vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview'; import { Range } from 'vs/editor/common/core/range'; suite('BulkEditPreview', function () { diff --git a/src/vs/workbench/test/browser/api/mainThreadEditors.test.ts b/src/vs/workbench/test/browser/api/mainThreadEditors.test.ts index d0f65701a26..f4d82ca1786 100644 --- a/src/vs/workbench/test/browser/api/mainThreadEditors.test.ts +++ b/src/vs/workbench/test/browser/api/mainThreadEditors.test.ts @@ -20,7 +20,7 @@ import { Position } from 'vs/editor/common/core/position'; import { IModelService } from 'vs/editor/common/services/modelService'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { TestFileService, TestEditorService, TestEditorGroupsService, TestEnvironmentService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { BulkEditService } from 'vs/workbench/services/bulkEdit/browser/bulkEditService'; +import { BulkEditService } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditService'; import { NullLogService, ILogService } from 'vs/platform/log/common/log'; import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; import { IReference, ImmortalReference } from 'vs/base/common/lifecycle'; diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 4a06e4a5ffc..6b38f0bcc3c 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -55,7 +55,6 @@ import 'vs/workbench/browser/parts/views/viewsService'; import 'vs/platform/undoRedo/common/undoRedoService'; import 'vs/workbench/services/uriIdentity/common/uriIdentityService'; import 'vs/workbench/services/extensions/browser/extensionUrlHandler'; -import 'vs/workbench/services/bulkEdit/browser/bulkEditService'; import 'vs/workbench/services/keybinding/common/keybindingEditing'; import 'vs/workbench/services/decorations/browser/decorationsService'; import 'vs/workbench/services/progress/browser/progressService'; @@ -165,7 +164,8 @@ import 'vs/workbench/contrib/files/browser/files.contribution'; import 'vs/workbench/contrib/backup/common/backup.contribution'; // bulkEdit -import 'vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution'; +import 'vs/workbench/contrib/bulkEdit/browser/bulkEditService'; +import 'vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution'; // Search import 'vs/workbench/contrib/search/browser/search.contribution'; From f67d4ddacd897a6d0f0f01023d078263b8c403ec Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Tue, 25 Aug 2020 14:08:50 +0200 Subject: [PATCH 505/736] Do not show auxiliary command in palette (fixes #105107) --- .../browser/extensions.contribution.ts | 5 +--- .../extensions/browser/extensionsActions.ts | 23 ------------------- .../welcome/page/browser/welcomePage.ts | 15 ++++++++++-- 3 files changed, 14 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 547b5274ccc..4665a96c74e 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -19,7 +19,7 @@ import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/brow import { OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction, ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, ShowBuiltInExtensionsAction, UpdateAllAction, - EnableAllAction, EnableAllWorkspaceAction, DisableAllAction, DisableAllWorkspaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, ShowAzureExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ConfigureRecommendedExtensionsCommandsContributor, InstallVSIXAction, ReinstallAction, InstallSpecificVersionOfExtensionAction, ClearExtensionsSearchResultsAction + EnableAllAction, EnableAllWorkspaceAction, DisableAllAction, DisableAllWorkspaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ConfigureRecommendedExtensionsCommandsContributor, InstallVSIXAction, ReinstallAction, InstallSpecificVersionOfExtensionAction, ClearExtensionsSearchResultsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; import { ExtensionEditor } from 'vs/workbench/contrib/extensions/browser/extensionEditor'; @@ -113,9 +113,6 @@ actionRegistry.registerWorkbenchAction(keymapRecommendationsActionDescriptor, 'P const languageExtensionsActionDescriptor = SyncActionDescriptor.from(ShowLanguageExtensionsAction); actionRegistry.registerWorkbenchAction(languageExtensionsActionDescriptor, 'Preferences: Language Extensions', PreferencesLabel); -const azureExtensionsActionDescriptor = SyncActionDescriptor.from(ShowAzureExtensionsAction); -actionRegistry.registerWorkbenchAction(azureExtensionsActionDescriptor, 'Preferences: Azure Extensions', PreferencesLabel); - const popularActionDescriptor = SyncActionDescriptor.from(ShowPopularExtensionsAction); actionRegistry.registerWorkbenchAction(popularActionDescriptor, 'Extensions: Show Popular Extensions', ExtensionsLabel); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 16ca60f34a5..66df3029505 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -2075,29 +2075,6 @@ export class ShowLanguageExtensionsAction extends Action { } } -export class ShowAzureExtensionsAction extends Action { - - static readonly ID = 'workbench.extensions.action.showAzureExtensions'; - static readonly LABEL = localize('showAzureExtensionsShort', "Azure Extensions"); - - constructor( - id: string, - label: string, - @IViewletService private readonly viewletService: IViewletService - ) { - super(id, label, undefined, true); - } - - run(): Promise { - return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) - .then(viewlet => { - viewlet.search('@sort:installs azure '); - viewlet.focus(); - }); - } -} - export class SearchCategoryAction extends Action { constructor( diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index 6378a16dddf..67c86cd6a89 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -7,7 +7,7 @@ import 'vs/css!./welcomePage'; import 'vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page'; import { URI } from 'vs/base/common/uri'; import * as strings from 'vs/base/common/strings'; -import { ICommandService } from 'vs/platform/commands/common/commands'; +import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import * as arrays from 'vs/base/common/arrays'; import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -31,7 +31,7 @@ import { splitName } from 'vs/base/common/labels'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { registerColor, focusBorder, textLinkForeground, textLinkActiveForeground, foreground, descriptionForeground, contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { getExtraColor } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughUtils'; -import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IExtensionsViewPaneContainer, IExtensionsWorkbenchService, VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { IEditorInputFactory, EditorInput } from 'vs/workbench/common/editor'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { TimeoutTimer } from 'vs/base/common/async'; @@ -46,6 +46,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IProductService } from 'vs/platform/product/common/productService'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; const configurationKey = 'workbench.startupEditor'; const oldConfigurationKey = 'workbench.welcome.enabled'; @@ -221,6 +222,16 @@ const extensionPackStrings: Strings = { extensionNotFound: localize('welcomePage.extensionPackNotFound', "Support for {0} with id {1} could not be found."), }; +CommandsRegistry.registerCommand('workbench.extensions.action.showAzureExtensions', accessor => { + const viewletService = accessor.get(IViewletService); + return viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) + .then(viewlet => { + viewlet.search('@sort:installs azure '); + viewlet.focus(); + }); +}); + /* __GDPR__ "installKeymap" : { "${include}": [ From 2e4bb09f426df259729d3bad9a152befb265cb14 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 25 Aug 2020 14:09:49 +0200 Subject: [PATCH 506/736] :lipstick: service name --- src/vs/workbench/services/path/common/pathService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/path/common/pathService.ts b/src/vs/workbench/services/path/common/pathService.ts index c2e1b21bd5a..8d877ba3746 100644 --- a/src/vs/workbench/services/path/common/pathService.ts +++ b/src/vs/workbench/services/path/common/pathService.ts @@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; -export const IPathService = createDecorator('path'); +export const IPathService = createDecorator('pathService'); /** * Provides access to path related properties that will match the From b0f0a1bf81b876e8f400b29fa88aa5e3413d67cd Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 25 Aug 2020 14:45:37 +0200 Subject: [PATCH 507/736] web - only add window indicator if running without remote --- src/vs/code/browser/workbench/workbench.ts | 31 ++++++++++++++++++---- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index cd315f77cc0..4b0ac90a35a 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -16,6 +16,7 @@ import { isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/windows/common/wi import { isEqual } from 'vs/base/common/resources'; import { isStandalone } from 'vs/base/browser/browser'; import { localize } from 'vs/nls'; +import { Schemas } from 'vs/base/common/network'; interface ICredential { service: string; @@ -277,6 +278,20 @@ class WorkspaceProvider implements IWorkspaceProvider { return false; } + + hasRemote(): boolean { + if (this.workspace) { + if (isFolderToOpen(this.workspace)) { + return this.workspace.folderUri.scheme === Schemas.vscodeRemote; + } + + if (isWorkspaceToOpen(this.workspace)) { + return this.workspace.workspaceUri.scheme === Schemas.vscodeRemote; + } + } + + return true; + } } class WindowIndicator implements IWindowIndicator { @@ -391,6 +406,9 @@ class WindowIndicator implements IWindowIndicator { } } + // Workspace Provider + const workspaceProvider = new WorkspaceProvider(workspace, payload); + // Home Indicator const homeIndicator: IHomeIndicator = { href: 'https://github.com/Microsoft/vscode', @@ -401,10 +419,13 @@ class WindowIndicator implements IWindowIndicator { // Commands const commands: ICommand[] = []; - // Window indicator - const windowIndicator = new WindowIndicator(workspace); - if (windowIndicator.commandImpl) { - commands.push(windowIndicator.commandImpl); + // Window indicator (unless connected to a remote) + let windowIndicator: WindowIndicator | undefined = undefined; + if (!workspaceProvider.hasRemote()) { + windowIndicator = new WindowIndicator(workspace); + if (windowIndicator.commandImpl) { + commands.push(windowIndicator.commandImpl); + } } // Product Quality Change Handler @@ -429,7 +450,7 @@ class WindowIndicator implements IWindowIndicator { commands, windowIndicator, productQualityChangeHandler, - workspaceProvider: new WorkspaceProvider(workspace, payload), + workspaceProvider, urlCallbackProvider: new PollingURLCallbackProvider(), credentialsProvider: new LocalStorageCredentialsProvider() }); From b2f3bf90758a6c62003a97d71faa0332ff5f4662 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 25 Aug 2020 15:20:32 +0200 Subject: [PATCH 508/736] fix https://github.com/microsoft/vscode/issues/105351 --- src/vs/vscode.d.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 229d11ad3d1..a3884ab8bf9 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -3086,6 +3086,8 @@ declare module 'vscode' { * @param uri Uri of the new file.. * @param options Defines if an existing file should be overwritten or be * ignored. When overwrite and ignoreIfExists are both set overwrite wins. + * When both are unset and when the file already exists then the edit cannot + * be applied successfully. * @param metadata Optional metadata for the entry. */ createFile(uri: Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean }, metadata?: WorkspaceEditEntryMetadata): void; From 81204145b50ac6fa27d1e00e2b53c811ab0524f9 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 25 Aug 2020 16:35:09 +0200 Subject: [PATCH 509/736] Fix #104610 --- extensions/configuration-editing/package.json | 2 +- .../browser/configurationService.ts | 73 ++++++++++++------- 2 files changed, 46 insertions(+), 29 deletions(-) diff --git a/extensions/configuration-editing/package.json b/extensions/configuration-editing/package.json index 3cadb80fd33..477d530a0ed 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -54,7 +54,7 @@ "url": "vscode://schemas/keybindings" }, { - "fileMatch": "vscode://defaultsettings/defaultSettings.json", + "fileMatch": "vscode://defaultsettings/*/*.json", "url": "vscode://schemas/settings/default" }, { diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 74518d7db33..650b4c9fad3 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -22,12 +22,14 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ConfigurationEditingService, EditableConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditingService'; import { WorkspaceConfiguration, FolderConfiguration, RemoteUserConfiguration, UserConfiguration } from 'vs/workbench/services/configuration/browser/configuration'; import { JSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditingService'; -import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; import { isEqual, dirname } from 'vs/base/common/resources'; import { mark } from 'vs/base/common/performance'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IFileService } from 'vs/platform/files/common/files'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; export class WorkspaceService extends Disposable implements IConfigurationService, IWorkspaceContextService { @@ -101,7 +103,6 @@ export class WorkspaceService extends Disposable implements IConfigurationServic }); })); - this._register(configurationRegistry.onDidSchemaChange(e => this.registerConfigurationSchemas())); this._register(configurationRegistry.onDidUpdateConfiguration(configurationProperties => this.onDefaultConfigurationChanged(configurationProperties))); this.workspaceEditingQueue = new Queue(); @@ -426,7 +427,6 @@ export class WorkspaceService extends Disposable implements IConfigurationServic } private initializeConfiguration(): Promise { - this.registerConfigurationSchemas(); return this.initializeUserConfiguration() .then(({ local, remote }) => this.loadConfiguration(local, remote)); } @@ -501,7 +501,6 @@ export class WorkspaceService extends Disposable implements IConfigurationServic private onDefaultConfigurationChanged(keys: string[]): void { this.defaultConfiguration = new DefaultConfigurationModel(); - this.registerConfigurationSchemas(); if (this.workspace) { const previousData = this._configuration.toData(); const change = this._configuration.compareAndUpdateDefaultConfiguration(this.defaultConfiguration, keys); @@ -528,30 +527,6 @@ export class WorkspaceService extends Disposable implements IConfigurationServic } } - private registerConfigurationSchemas(): void { - if (this.workspace) { - const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); - const defaultSettingsSchema: IJSONSchema = { additionalProperties: true, allowTrailingCommas: true, allowComments: true }; - const allSettingsSchema: IJSONSchema = { properties: allSettings.properties, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true }; - const userSettingsSchema: IJSONSchema = this.remoteUserConfiguration ? { properties: { ...applicationSettings.properties, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true } : allSettingsSchema; - const machineSettingsSchema: IJSONSchema = { properties: { ...machineSettings.properties, ...machineOverridableSettings.properties, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true }; - const workspaceSettingsSchema: IJSONSchema = { properties: { ...machineOverridableSettings.properties, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true }; - - jsonRegistry.registerSchema(defaultSettingsSchemaId, defaultSettingsSchema); - jsonRegistry.registerSchema(userSettingsSchemaId, userSettingsSchema); - jsonRegistry.registerSchema(machineSettingsSchemaId, machineSettingsSchema); - - if (WorkbenchState.WORKSPACE === this.getWorkbenchState()) { - const folderSettingsSchema: IJSONSchema = { properties: { ...machineOverridableSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true }; - jsonRegistry.registerSchema(workspaceSettingsSchemaId, workspaceSettingsSchema); - jsonRegistry.registerSchema(folderSettingsSchemaId, folderSettingsSchema); - } else { - jsonRegistry.registerSchema(workspaceSettingsSchemaId, workspaceSettingsSchema); - jsonRegistry.registerSchema(folderSettingsSchemaId, workspaceSettingsSchema); - } - } - } - private onLocalUserConfigurationChanged(userConfiguration: ConfigurationModel): void { const previous = { data: this._configuration.toData(), workspace: this.workspace }; const change = this._configuration.compareAndUpdateLocalUserConfiguration(userConfiguration); @@ -777,3 +752,45 @@ export class WorkspaceService extends Disposable implements IConfigurationServic return null; } } + +class RegisterConfigurationSchemasContribution extends Disposable implements IWorkbenchContribution { + constructor( + @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, + @IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService, + ) { + super(); + this.registerConfigurationSchemas(); + const configurationRegistry = Registry.as(Extensions.Configuration); + this._register(configurationRegistry.onDidUpdateConfiguration(e => this.registerConfigurationSchemas())); + this._register(configurationRegistry.onDidSchemaChange(e => this.registerConfigurationSchemas())); + } + + private registerConfigurationSchemas(): void { + const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); + const allSettingsSchema: IJSONSchema = { properties: allSettings.properties, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true }; + const userSettingsSchema: IJSONSchema = this.workbenchEnvironmentService.configuration.remoteAuthority ? { properties: { ...applicationSettings.properties, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true } : allSettingsSchema; + const machineSettingsSchema: IJSONSchema = { properties: { ...machineSettings.properties, ...machineOverridableSettings.properties, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true }; + const workspaceSettingsSchema: IJSONSchema = { properties: { ...machineOverridableSettings.properties, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true }; + + jsonRegistry.registerSchema(defaultSettingsSchemaId, { + properties: Object.keys(allSettings.properties).reduce((result, key) => { result[key] = { ...allSettings.properties[key], deprecationMessage: undefined }; return result; }, {}), + patternProperties: Object.keys(allSettings.patternProperties).reduce((result, key) => { result[key] = { ...allSettings.patternProperties[key], deprecationMessage: undefined }; return result; }, {}), + additionalProperties: true, + allowTrailingCommas: true, + allowComments: true + }); + jsonRegistry.registerSchema(userSettingsSchemaId, userSettingsSchema); + jsonRegistry.registerSchema(machineSettingsSchemaId, machineSettingsSchema); + + if (WorkbenchState.WORKSPACE === this.workspaceContextService.getWorkbenchState()) { + const folderSettingsSchema: IJSONSchema = { properties: { ...machineOverridableSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true }; + jsonRegistry.registerSchema(workspaceSettingsSchemaId, workspaceSettingsSchema); + jsonRegistry.registerSchema(folderSettingsSchemaId, folderSettingsSchema); + } else { + jsonRegistry.registerSchema(workspaceSettingsSchemaId, workspaceSettingsSchema); + jsonRegistry.registerSchema(folderSettingsSchemaId, workspaceSettingsSchema); + } + } +} + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(RegisterConfigurationSchemasContribution, LifecyclePhase.Restored); From 6aab9f199e1485056dfae67de91c8a11606ea2d1 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 25 Aug 2020 08:34:22 -0700 Subject: [PATCH 510/736] lifecycle & disable scm notebook --- .../notebook/browser/contrib/scm/scm.ts | 72 +++++++++++-------- .../notebook/browser/notebook.contribution.ts | 2 +- .../notebook/browser/notebookBrowser.ts | 1 + .../notebook/browser/notebookEditorWidget.ts | 3 + .../notebook/test/testNotebookEditor.ts | 1 + 5 files changed, 47 insertions(+), 32 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts b/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts index e43a7a8ba74..3b3601c8d7f 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts @@ -4,14 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { INotebookEditorContribution, INotebookEditor, INotebookDeltaDecoration } from '../../notebookBrowser'; +import { INotebookEditorContribution, INotebookEditor } from '../../notebookBrowser'; import { registerNotebookContribution } from '../../notebookEditorExtensions'; import { ISCMService } from 'vs/workbench/contrib/scm/common/scm'; import { createProviderComparer } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; import { first, ThrottledDelayer } from 'vs/base/common/async'; import { INotebookService } from '../../../common/notebookService'; -import { LcsDiff } from 'vs/base/common/diff/diff'; -import { CellSequence } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { FileService } from 'vs/platform/files/common/fileService'; import { IFileService } from 'vs/platform/files/common/files'; @@ -40,6 +38,7 @@ export class SCMController extends Disposable implements INotebookEditorContribu if (!this._notebookEditor.isEmbedded) { this._register(this._notebookEditor.onDidChangeModel(() => { this._localDisposable.clear(); + this._originalResourceDisposableStore.clear(); this._diffDelayer.cancel(); this.update(); @@ -54,6 +53,11 @@ export class SCMController extends Disposable implements INotebookEditorContribu } })); + this._register(this._notebookEditor.onWillDispose(() => { + this._localDisposable.clear(); + this._originalResourceDisposableStore.clear(); + })); + this.update(); } } @@ -87,6 +91,12 @@ export class SCMController extends Disposable implements INotebookEditorContribu })); const originalDocument = await this._notebookService.resolveNotebook(viewType, result, false); + this._originalResourceDisposableStore.add({ + dispose: () => { + this._originalDocument?.dispose(); + this._originalDocument = undefined; + } + }); this._originalDocument = originalDocument; } @@ -115,37 +125,37 @@ export class SCMController extends Disposable implements INotebookEditorContribu return; } - const diff = new LcsDiff(new CellSequence(this._originalDocument), new CellSequence(modifiedDocument)); - const diffResult = diff.ComputeDiff(false); + // const diff = new LcsDiff(new CellSequence(this._originalDocument), new CellSequence(modifiedDocument)); + // const diffResult = diff.ComputeDiff(false); - const decorations: INotebookDeltaDecoration[] = []; - diffResult.changes.forEach(change => { - if (change.originalLength === 0) { - // doesn't exist in original - for (let i = 0; i < change.modifiedLength; i++) { - decorations.push({ - handle: modifiedDocument.cells[change.modifiedStart + i].handle, - options: { gutterClassName: 'nb-gutter-cell-inserted' } - }); - } - } else { - if (change.modifiedLength === 0) { - // diff.deleteCount - // removed from original - } else { - // modification - for (let i = 0; i < change.modifiedLength; i++) { - decorations.push({ - handle: modifiedDocument.cells[change.modifiedStart + i].handle, - options: { gutterClassName: 'nb-gutter-cell-changed' } - }); - } - } - } - }); + // const decorations: INotebookDeltaDecoration[] = []; + // diffResult.changes.forEach(change => { + // if (change.originalLength === 0) { + // // doesn't exist in original + // for (let i = 0; i < change.modifiedLength; i++) { + // decorations.push({ + // handle: modifiedDocument.cells[change.modifiedStart + i].handle, + // options: { gutterClassName: 'nb-gutter-cell-inserted' } + // }); + // } + // } else { + // if (change.modifiedLength === 0) { + // // diff.deleteCount + // // removed from original + // } else { + // // modification + // for (let i = 0; i < change.modifiedLength; i++) { + // decorations.push({ + // handle: modifiedDocument.cells[change.modifiedStart + i].handle, + // options: { gutterClassName: 'nb-gutter-cell-changed' } + // }); + // } + // } + // } + // }); - this._lastDecorationId = this._notebookEditor.deltaCellDecorations(this._lastDecorationId, decorations); + // this._lastDecorationId = this._notebookEditor.deltaCellDecorations(this._lastDecorationId, decorations); }); } diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 036df080c73..6f0d4cba56b 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -56,7 +56,7 @@ import 'vs/workbench/contrib/notebook/browser/contrib/format/formatting'; import 'vs/workbench/contrib/notebook/browser/contrib/toc/tocProvider'; import 'vs/workbench/contrib/notebook/browser/contrib/marker/markerProvider'; import 'vs/workbench/contrib/notebook/browser/contrib/status/editorStatus'; -import 'vs/workbench/contrib/notebook/browser/contrib/scm/scm'; +// import 'vs/workbench/contrib/notebook/browser/contrib/scm/scm'; // Output renderers registration diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 32afacda32c..d7c106c9531 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -224,6 +224,7 @@ export interface INotebookEditor extends IEditor { readonly onDidChangeKernel: Event; readonly onDidChangeActiveCell: Event; readonly onDidScroll: Event; + readonly onWillDispose: Event; isDisposed: boolean; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 7a2a854f4e0..fc26daa688a 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -101,6 +101,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor public readonly onDidFocus = this._onDidFocusEmitter.event; private readonly _onWillScroll = this._register(new Emitter()); public readonly onWillScroll: Event = this._onWillScroll.event; + private readonly _onWillDispose = this._register(new Emitter()); + public readonly onWillDispose: Event = this._onWillDispose.event; set scrollTop(top: number) { if (this._list) { @@ -1672,6 +1674,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor dispose() { this._isDisposed = true; + this._onWillDispose.fire(); // dispose webview first this._webview?.dispose(); diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index 77f8ab26eb2..7f69f420dd2 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -77,6 +77,7 @@ export class TestNotebookEditor implements INotebookEditor { onDidChangeAvailableKernels: Event = new Emitter().event; onDidChangeActiveCell: Event = new Emitter().event; onDidScroll = new Emitter().event; + onWillDispose = new Emitter().event; uri?: URI | undefined; textModel?: NotebookTextModel | undefined; From 7cc7041cf38e59990a9388a9f080d03671da13e3 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Tue, 25 Aug 2020 17:36:28 +0200 Subject: [PATCH 511/736] finalize DAP breakpoint mapping API; fixes #99716 --- src/vs/vscode.d.ts | 44 +++++++++++++++++++++++++------------ src/vs/vscode.proposed.d.ts | 18 --------------- 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index a3884ab8bf9..ffa13e24bfc 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -10699,6 +10699,27 @@ declare module 'vscode' { export function createSourceControl(id: string, label: string, rootUri?: Uri): SourceControl; } + /** + * A DebugProtocolMessage is an opaque stand-in type for the [ProtocolMessage](https://microsoft.github.io/debug-adapter-protocol/specification#Base_Protocol_ProtocolMessage) type defined in the Debug Adapter Protocol. + */ + export interface DebugProtocolMessage { + // Properties: see details [here](https://microsoft.github.io/debug-adapter-protocol/specification#Base_Protocol_ProtocolMessage). + } + + /** + * A DebugProtocolSource is an opaque stand-in type for the [Source](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Source) type defined in the Debug Adapter Protocol. + */ + export interface DebugProtocolSource { + // Properties: see details [here](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Source). + } + + /** + * A DebugProtocolBreakpoint is an opaque stand-in type for the [Breakpoint](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Breakpoint) type defined in the Debug Adapter Protocol. + */ + export interface DebugProtocolBreakpoint { + // Properties: see details [here](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Breakpoint). + } + /** * Configuration for a debug session. */ @@ -10762,6 +10783,15 @@ declare module 'vscode' { * Send a custom request to the debug adapter. */ customRequest(command: string, args?: any): Thenable; + + /** + * Maps a VS Code breakpoint to the corresponding Debug Adapter Protocol (DAP) breakpoint that is managed by the debug adapter of the debug session. + * If no DAP breakpoint exists (either because the VS Code breakpoint was not yet registered or because the debug adapter is not interested in the breakpoint), the value `undefined` is returned. + * + * @param breakpoint A VS Code [breakpoint](#Breakpoint). + * @return A promise that resolves to the Debug Adapter Protocol breakpoint or `undefined`. + */ + getDebugProtocolBreakpoint(breakpoint: Breakpoint): Thenable; } /** @@ -10937,13 +10967,6 @@ declare module 'vscode' { handleMessage(message: DebugProtocolMessage): void; } - /** - * A DebugProtocolMessage is an opaque stand-in type for the [ProtocolMessage](https://microsoft.github.io/debug-adapter-protocol/specification#Base_Protocol_ProtocolMessage) type defined in the Debug Adapter Protocol. - */ - export interface DebugProtocolMessage { - // Properties: see details [here](https://microsoft.github.io/debug-adapter-protocol/specification#Base_Protocol_ProtocolMessage). - } - /** * A debug adapter descriptor for an inline implementation. */ @@ -11165,13 +11188,6 @@ declare module 'vscode' { compact?: boolean; } - /** - * A DebugProtocolSource is an opaque stand-in type for the [Source](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Source) type defined in the Debug Adapter Protocol. - */ - export interface DebugProtocolSource { - // Properties: see details [here](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Source). - } - /** * A DebugConfigurationProviderTriggerKind specifies when the `provideDebugConfigurations` method of a `DebugConfigurationProvider` is triggered. * Currently there are two situations: to provide the initial debug configurations for a newly created launch.json or diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 058cebad5b7..913722c75a6 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -737,24 +737,6 @@ declare module 'vscode' { //#region debug - /** - * A DebugProtocolBreakpoint is an opaque stand-in type for the [Breakpoint](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Breakpoint) type defined in the Debug Adapter Protocol. - */ - export interface DebugProtocolBreakpoint { - // Properties: see details [here](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Breakpoint). - } - - export interface DebugSession { - /** - * Maps a VS Code breakpoint to the corresponding Debug Adapter Protocol (DAP) breakpoint that is managed by the debug adapter of the debug session. - * If no DAP breakpoint exists (either because the VS Code breakpoint was not yet registered or because the debug adapter is not interested in the breakpoint), the value `undefined` is returned. - * - * @param breakpoint A VS Code [breakpoint](#Breakpoint). - * @return A promise that resolves to the Debug Adapter Protocol breakpoint or `undefined`. - */ - getDebugProtocolBreakpoint(breakpoint: Breakpoint): Thenable; - } - // deprecated debug API export interface DebugConfigurationProvider { From 8817251691ce83900ad6246c3bfb2468e39b7141 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 24 Aug 2020 11:35:26 -0700 Subject: [PATCH 512/736] debug: provide positive ack to js-debug bootloader on auto attach --- extensions/debug-auto-launch/src/extension.ts | 12 ++++++++++-- product.json | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/extensions/debug-auto-launch/src/extension.ts b/extensions/debug-auto-launch/src/extension.ts index f3527df7125..b70fb3dedbe 100644 --- a/extensions/debug-auto-launch/src/extension.ts +++ b/extensions/debug-auto-launch/src/extension.ts @@ -191,14 +191,22 @@ const transitions: { [S in State]: StateTransition } = { const server = await new Promise((resolve, reject) => { const s = createServer((socket) => { let data: Buffer[] = []; - socket.on('data', (chunk) => data.push(chunk)); - socket.on('end', async () => { + socket.on('data', async (chunk) => { + if (chunk[chunk.length - 1] !== 0) { // terminated with NUL byte + data.push(chunk); + return; + } + + data.push(chunk.slice(0, -1)); + try { await vscode.commands.executeCommand( 'extension.js-debug.autoAttachToProcess', JSON.parse(Buffer.concat(data).toString()) ); + socket.write(Buffer.from([0])); } catch (err) { + socket.write(Buffer.from([1])); console.error(err); } }); diff --git a/product.json b/product.json index b72e7e53dee..c6c74ce1f22 100644 --- a/product.json +++ b/product.json @@ -91,7 +91,7 @@ }, { "name": "ms-vscode.js-debug", - "version": "1.49.0", + "version": "1.49.2", "repo": "https://github.com/Microsoft/vscode-js-debug", "metadata": { "id": "25629058-ddac-4e17-abba-74678e126c5d", From 8ca857a92eab6ea49210d032c510edbe3fabe4e1 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Fri, 14 Aug 2020 02:18:04 -0400 Subject: [PATCH 513/736] Closes #97544 - adds in operator to when clauses --- .../platform/contextkey/common/contextkey.ts | 132 +++++++++++++++++- 1 file changed, 130 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts index 55e154645ec..9f1fbde97a2 100644 --- a/src/vs/platform/contextkey/common/contextkey.ts +++ b/src/vs/platform/contextkey/common/contextkey.ts @@ -27,7 +27,9 @@ export const enum ContextKeyExprType { And = 6, Regex = 7, NotRegex = 8, - Or = 9 + Or = 9, + In = 10, + NotIn = 11, } export interface IContextKeyExprMapper { @@ -36,6 +38,7 @@ export interface IContextKeyExprMapper { mapEquals(key: string, value: any): ContextKeyExpression; mapNotEquals(key: string, value: any): ContextKeyExpression; mapRegex(key: string, regexp: RegExp | null): ContextKeyRegexExpr; + mapIn(key: string, valueKey: string): ContextKeyInExpr; } export interface IContextKeyExpression { @@ -52,7 +55,7 @@ export interface IContextKeyExpression { export type ContextKeyExpression = ( ContextKeyFalseExpr | ContextKeyTrueExpr | ContextKeyDefinedExpr | ContextKeyNotExpr | ContextKeyEqualsExpr | ContextKeyNotEqualsExpr | ContextKeyRegexExpr - | ContextKeyNotRegexExpr | ContextKeyAndExpr | ContextKeyOrExpr + | ContextKeyNotRegexExpr | ContextKeyAndExpr | ContextKeyOrExpr | ContextKeyInExpr | ContextKeyNotInExpr ); export abstract class ContextKeyExpr { @@ -81,6 +84,10 @@ export abstract class ContextKeyExpr { return ContextKeyRegexExpr.create(key, value); } + public static in(key: string, value: string): ContextKeyExpression { + return ContextKeyInExpr.create(key, value); + } + public static not(key: string): ContextKeyExpression { return ContextKeyNotExpr.create(key); } @@ -129,6 +136,11 @@ export abstract class ContextKeyExpr { return ContextKeyRegexExpr.create(pieces[0].trim(), this._deserializeRegexValue(pieces[1], strict)); } + if (serializedOne.indexOf(' in ') >= 0) { + let pieces = serializedOne.split(' in '); + return ContextKeyInExpr.create(pieces[0].trim(), this._deserializeValue(pieces[1], strict)); + } + if (/^\!\s*/.test(serializedOne)) { return ContextKeyNotExpr.create(serializedOne.substr(1).trim()); } @@ -393,6 +405,122 @@ export class ContextKeyEqualsExpr implements IContextKeyExpression { } } +export class ContextKeyInExpr implements IContextKeyExpression { + + public static create(key: string, valueKey: string): ContextKeyExpression { + return new ContextKeyInExpr(key, valueKey); + } + + public readonly type = ContextKeyExprType.In; + + private constructor(private readonly key: string, private readonly valueKey: string) { + } + + public cmp(other: ContextKeyExpression): number { + if (other.type !== this.type) { + return this.type - other.type; + } + if (this.key < other.key) { + return -1; + } + if (this.key > other.key) { + return 1; + } + if (this.valueKey < other.valueKey) { + return -1; + } + if (this.valueKey > other.valueKey) { + return 1; + } + return 0; + } + + public equals(other: ContextKeyExpression): boolean { + if (other.type === this.type) { + return (this.key === other.key && this.valueKey === other.valueKey); + } + return false; + } + + public evaluate(context: IContext): boolean { + const source = context.getValue(this.valueKey); + + const item = context.getValue(this.key); + + if (Array.isArray(source)) { + return source.includes(item); + } + + if (typeof item === 'string' && typeof source === 'object' && source !== undefined && source !== null) { + return item in source; + } + return false; + } + + public serialize(): string { + return this.key + ' in \'' + this.valueKey + '\''; + } + + public keys(): string[] { + return [this.key]; + } + + public map(mapFnc: IContextKeyExprMapper): ContextKeyInExpr { + return mapFnc.mapIn(this.key, this.valueKey); + } + + public negate(): ContextKeyExpression { + return ContextKeyNotInExpr.create(this); + } +} + +export class ContextKeyNotInExpr implements IContextKeyExpression { + + public static create(actual: ContextKeyInExpr): ContextKeyExpression { + return new ContextKeyNotInExpr(actual); + } + + public readonly type = ContextKeyExprType.NotIn; + + private constructor(private readonly _actual: ContextKeyInExpr) { + // + } + + public cmp(other: ContextKeyExpression): number { + if (other.type !== this.type) { + return this.type - other.type; + } + return this._actual.cmp(other._actual); + } + + public equals(other: ContextKeyExpression): boolean { + if (other.type === this.type) { + return this._actual.equals(other._actual); + } + return false; + } + + public evaluate(context: IContext): boolean { + return !this._actual.evaluate(context); + } + + public serialize(): string { + throw new Error('Method not implemented.'); + } + + public keys(): string[] { + return this._actual.keys(); + } + + public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression { + return new ContextKeyNotInExpr(this._actual.map(mapFnc)); + } + + public negate(): ContextKeyExpression { + return this._actual; + } +} + export class ContextKeyNotEqualsExpr implements IContextKeyExpression { public static create(key: string, value: any): ContextKeyExpression { From 2057d8abb2bc0e7d6fc74c98f2ddda1cdd83f46e Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 25 Aug 2020 15:46:40 +0200 Subject: [PATCH 514/736] Fix build --- src/vs/platform/contextkey/common/contextkey.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts index 9f1fbde97a2..2cae5106d8a 100644 --- a/src/vs/platform/contextkey/common/contextkey.ts +++ b/src/vs/platform/contextkey/common/contextkey.ts @@ -448,7 +448,7 @@ export class ContextKeyInExpr implements IContextKeyExpression { const item = context.getValue(this.key); if (Array.isArray(source)) { - return source.includes(item); + return (source.indexOf(item) >= 0); } if (typeof item === 'string' && typeof source === 'object' && source !== undefined && source !== null) { From d233bf34bc248bb32c684f27a75aa5654d9f6848 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 25 Aug 2020 18:11:25 +0200 Subject: [PATCH 515/736] - Add tests - RHS of `in` expression is not a value, so don't deserialize it like a value - avoid unnecessary undefined check - use `hasOwnProperty` for object case - keys() should include the RHS of `in` expression --- src/vs/platform/contextkey/common/contextkey.ts | 10 ++++++---- .../contextkey/test/common/contextkey.test.ts | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts index 2cae5106d8a..6f0fb98af61 100644 --- a/src/vs/platform/contextkey/common/contextkey.ts +++ b/src/vs/platform/contextkey/common/contextkey.ts @@ -17,6 +17,8 @@ STATIC_VALUES.set('isWindows', isWindows); STATIC_VALUES.set('isWeb', isWeb); STATIC_VALUES.set('isMacNative', isMacintosh && !isWeb); +const hasOwnProperty = Object.prototype.hasOwnProperty; + export const enum ContextKeyExprType { False = 0, True = 1, @@ -138,7 +140,7 @@ export abstract class ContextKeyExpr { if (serializedOne.indexOf(' in ') >= 0) { let pieces = serializedOne.split(' in '); - return ContextKeyInExpr.create(pieces[0].trim(), this._deserializeValue(pieces[1], strict)); + return ContextKeyInExpr.create(pieces[0].trim(), pieces[1].trim()); } if (/^\!\s*/.test(serializedOne)) { @@ -451,8 +453,8 @@ export class ContextKeyInExpr implements IContextKeyExpression { return (source.indexOf(item) >= 0); } - if (typeof item === 'string' && typeof source === 'object' && source !== undefined && source !== null) { - return item in source; + if (typeof item === 'string' && typeof source === 'object' && source !== null) { + return hasOwnProperty.call(source, item); } return false; } @@ -462,7 +464,7 @@ export class ContextKeyInExpr implements IContextKeyExpression { } public keys(): string[] { - return [this.key]; + return [this.key, this.valueKey]; } public map(mapFnc: IContextKeyExprMapper): ContextKeyInExpr { diff --git a/src/vs/platform/contextkey/test/common/contextkey.test.ts b/src/vs/platform/contextkey/test/common/contextkey.test.ts index c7784c888e1..042012f1d33 100644 --- a/src/vs/platform/contextkey/test/common/contextkey.test.ts +++ b/src/vs/platform/contextkey/test/common/contextkey.test.ts @@ -150,4 +150,19 @@ suite('ContextKeyExpr', () => { t('a || b', 'c && d', 'a && c && d || b && c && d'); t('a || b', 'c && d || e', 'a && e || b && e || a && c && d || b && c && d'); }); + + test('ContextKeyInExpr', () => { + const ainb = ContextKeyExpr.deserialize('a in b')!; + assert.equal(ainb.evaluate(createContext({ 'a': 3, 'b': [3, 2, 1] })), true); + assert.equal(ainb.evaluate(createContext({ 'a': 3, 'b': [1, 2, 3] })), true); + assert.equal(ainb.evaluate(createContext({ 'a': 3, 'b': [1, 2] })), false); + assert.equal(ainb.evaluate(createContext({ 'a': 3 })), false); + assert.equal(ainb.evaluate(createContext({ 'a': 3, 'b': null })), false); + assert.equal(ainb.evaluate(createContext({ 'a': 'x', 'b': ['x'] })), true); + assert.equal(ainb.evaluate(createContext({ 'a': 'x', 'b': ['y'] })), false); + assert.equal(ainb.evaluate(createContext({ 'a': 'x', 'b': {} })), false); + assert.equal(ainb.evaluate(createContext({ 'a': 'x', 'b': { 'x': false } })), true); + assert.equal(ainb.evaluate(createContext({ 'a': 'x', 'b': { 'x': true } })), true); + assert.equal(ainb.evaluate(createContext({ 'a': 'prototype', 'b': {} })), false); + }); }); From 55e368c912572d057cb28808015c05f31eafdde8 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 25 Aug 2020 08:49:55 -0700 Subject: [PATCH 516/736] bundle the worker. --- build/gulpfile.vscode.js | 1 + src/buildfile.js | 1 + 2 files changed, 2 insertions(+) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 3eb2cfdc4fe..2c86b2808e0 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -43,6 +43,7 @@ const vscodeEntryPoints = _.flatten([ buildfile.entrypoint('vs/workbench/workbench.desktop.main'), buildfile.base, buildfile.workerExtensionHost, + buildfile.workerNotebook, buildfile.workbenchDesktop, buildfile.code ]); diff --git a/src/buildfile.js b/src/buildfile.js index 2df9bf24e73..f6d1c647d9d 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -16,6 +16,7 @@ exports.base = [{ }]; exports.workerExtensionHost = [entrypoint('vs/workbench/services/extensions/worker/extensionHostWorker')]; +exports.workerNotebook = [entrypoint('vs/workbench/contrib/notebook/common/services/notebookSimpleWorker')]; exports.workbenchDesktop = require('./vs/workbench/buildfile.desktop').collectModules(); exports.workbenchWeb = require('./vs/workbench/buildfile.web').collectModules(); From b373309ab7b1273a5524fdc664591329f84394a3 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 25 Aug 2020 10:45:29 -0700 Subject: [PATCH 517/736] remove kernel from content provider. --- src/vs/vscode.proposed.d.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 913722c75a6..6ff3c4e2523 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1587,8 +1587,6 @@ declare module 'vscode' { saveNotebookAs(targetResource: Uri, document: NotebookDocument, cancellation: CancellationToken): Promise; readonly onDidChangeNotebook: Event; backupNotebook(document: NotebookDocument, context: NotebookDocumentBackupContext, cancellation: CancellationToken): Promise; - - kernel?: NotebookKernel; } export interface NotebookKernel { From 4b222a704f562aca1d52346533fda25d2009b2a1 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 24 Aug 2020 20:20:21 -0700 Subject: [PATCH 518/736] Revert "Revert "Use TS 4.1-nightly for building VS Code"" This reverts commit 4e96ceff54718ecf4ba0d181293ffe7adfda9aa6. --- build/package.json | 2 +- build/yarn.lock | 8 ++++---- package.json | 2 +- src/vs/platform/workspace/common/workspace.ts | 8 ++++---- src/vs/workbench/common/editor/editorGroup.ts | 2 +- yarn.lock | 8 ++++---- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/build/package.json b/build/package.json index 7392e4b602c..e185594554b 100644 --- a/build/package.json +++ b/build/package.json @@ -45,7 +45,7 @@ "minimist": "^1.2.3", "request": "^2.85.0", "terser": "4.3.8", - "typescript": "^4.0.1-rc", + "typescript": "^4.1.0-dev.20200824", "vsce": "1.48.0", "vscode-telemetry-extractor": "^1.6.0", "xml2js": "^0.4.17" diff --git a/build/yarn.lock b/build/yarn.lock index d610f9cefa7..01ebd186c4c 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -2535,10 +2535,10 @@ typescript@^3.0.1: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== -typescript@^4.0.1-rc: - version "4.0.1-rc" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.1-rc.tgz#8adc78223eae56fe71d906a5fa90c3543b07a677" - integrity sha512-TCkspT3dSKOykbzS3/WSK7pqU2h1d/lEO6i45Afm5Y3XNAEAo8YXTG3kHOQk/wFq/5uPyO1+X8rb/Q+g7UsxJw== +typescript@^4.1.0-dev.20200824: + version "4.1.0-dev.20200824" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.0-dev.20200824.tgz#34c92d9b6e5124600658c0d4e9b8c125beaf577d" + integrity sha512-hTJfocmebnMKoqRw/xs3bL61z87XXtvOUwYtM7zaCX9mAvnfdo1x1bzQlLZAsvdzRIgAHPJQYbqYHKygWkDw6g== typical@^4.0.0: version "4.0.0" diff --git a/package.json b/package.json index a50ce5cc307..1858ff663b8 100644 --- a/package.json +++ b/package.json @@ -164,7 +164,7 @@ "source-map": "^0.4.4", "style-loader": "^1.0.0", "ts-loader": "^4.4.2", - "typescript": "^4.0.1-rc", + "typescript": "^4.1.0-dev.20200824", "typescript-formatter": "7.1.0", "underscore": "^1.8.2", "vinyl": "^2.0.0", diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index 0f2e82d0e85..c0c34d74f57 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -82,9 +82,9 @@ export interface IWorkspaceFoldersChangeEvent { export namespace IWorkspace { export function isIWorkspace(thing: unknown): thing is IWorkspace { - return thing && typeof thing === 'object' + return !!(thing && typeof thing === 'object' && typeof (thing as IWorkspace).id === 'string' - && Array.isArray((thing as IWorkspace).folders); + && Array.isArray((thing as IWorkspace).folders)); } } @@ -127,10 +127,10 @@ export interface IWorkspaceFolderData { export namespace IWorkspaceFolder { export function isIWorkspaceFolder(thing: unknown): thing is IWorkspaceFolder { - return thing && typeof thing === 'object' + return !!(thing && typeof thing === 'object' && URI.isUri((thing as IWorkspaceFolder).uri) && typeof (thing as IWorkspaceFolder).name === 'string' - && typeof (thing as IWorkspaceFolder).toResource === 'function'; + && typeof (thing as IWorkspaceFolder).toResource === 'function'); } } diff --git a/src/vs/workbench/common/editor/editorGroup.ts b/src/vs/workbench/common/editor/editorGroup.ts index 57e5432ed19..97c2058102e 100644 --- a/src/vs/workbench/common/editor/editorGroup.ts +++ b/src/vs/workbench/common/editor/editorGroup.ts @@ -55,7 +55,7 @@ export interface ISerializedEditorGroup { export function isSerializedEditorGroup(obj?: unknown): obj is ISerializedEditorGroup { const group = obj as ISerializedEditorGroup; - return obj && typeof obj === 'object' && Array.isArray(group.editors) && Array.isArray(group.mru); + return !!(obj && typeof obj === 'object' && Array.isArray(group.editors) && Array.isArray(group.mru)); } export class EditorGroup extends Disposable { diff --git a/yarn.lock b/yarn.lock index b760c375232..e813c24b15d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9369,10 +9369,10 @@ typescript@^2.6.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4" integrity sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q= -typescript@^4.0.1-rc: - version "4.0.1-rc" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.1-rc.tgz#8adc78223eae56fe71d906a5fa90c3543b07a677" - integrity sha512-TCkspT3dSKOykbzS3/WSK7pqU2h1d/lEO6i45Afm5Y3XNAEAo8YXTG3kHOQk/wFq/5uPyO1+X8rb/Q+g7UsxJw== +typescript@^4.1.0-dev.20200824: + version "4.1.0-dev.20200824" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.0-dev.20200824.tgz#34c92d9b6e5124600658c0d4e9b8c125beaf577d" + integrity sha512-hTJfocmebnMKoqRw/xs3bL61z87XXtvOUwYtM7zaCX9mAvnfdo1x1bzQlLZAsvdzRIgAHPJQYbqYHKygWkDw6g== uc.micro@^1.0.1, uc.micro@^1.0.3: version "1.0.3" From 02bafd47bb732773b77882b1a15e2f2dd09fa987 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 25 Aug 2020 12:17:21 -0700 Subject: [PATCH 519/736] Support schemes with - in the name, such as `vscode-remote` --- src/vs/workbench/contrib/webview/browser/webviewElement.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index 792d98a57bb..993b0dfc59a 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -54,7 +54,7 @@ export class IFrameWebview extends BaseWebview implements Web this._register(this.on(WebviewMessageChannels.loadResource, (entry: any) => { const rawPath = entry.path; const normalizedPath = decodeURIComponent(rawPath); - const uri = URI.parse(normalizedPath.replace(/^\/(\w+)\/(.+)$/, (_, scheme, path) => scheme + ':/' + path)); + const uri = URI.parse(normalizedPath.replace(/^\/([\w\-]+)\/(.+)$/, (_, scheme, path) => scheme + ':/' + path)); this.loadResource(rawPath, uri); })); From ebccd57e8c8df701a7fe54a495b8ddaf7cb91c15 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 25 Aug 2020 11:09:11 -0700 Subject: [PATCH 520/736] insert/delete background colors for cells. --- .../notebook/browser/diff/cellComponents.ts | 6 +- .../browser/diff/notebookTextDiffEditor.ts | 68 ++++++++++++++++++- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index 7be518c7d2b..6feb0e77731 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -5,13 +5,14 @@ import * as DOM from 'vs/base/browser/dom'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { Range } from 'vs/editor/common/core/range'; import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { CellDiffViewModel, PropertyFoldingState } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; import { CellDiffRenderTemplate, CellDiffViewModelLayoutChangeEvent, DIFF_CELL_MARGIN, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/common'; import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; +import { DECORATIONS, DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; import { renderCodicons } from 'vs/base/common/codicons'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; @@ -19,6 +20,7 @@ import { format } from 'vs/base/common/jsonFormatter'; import { applyEdits } from 'vs/base/common/jsonEdit'; import { NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { hash } from 'vs/base/common/hash'; +import { Constants } from 'vs/base/common/uint'; const fixedDiffEditorOptions: IDiffEditorOptions = { @@ -604,6 +606,7 @@ export class DeletedCell extends AbstractCellRenderer { } styleContainer(container: HTMLElement) { + DOM.addClass(container, 'removed'); } buildSourceEditor(sourceContainer: HTMLElement): void { @@ -689,6 +692,7 @@ export class InsertCell extends AbstractCellRenderer { } styleContainer(container: HTMLElement): void { + DOM.addClass(container, 'inserted'); } buildSourceEditor(sourceContainer: HTMLElement): void { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index 3321a5bb2ca..2633d1e4c3c 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -18,7 +18,7 @@ import { CellDiffViewModel } from 'vs/workbench/contrib/notebook/browser/diff/ce import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { CellDiffRenderer, NotebookCellTextDiffListDelegate, NotebookTextDiffList } from 'vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { diffDiagonalFill, editorBackground, focusBorder, foreground } from 'vs/platform/theme/common/colorRegistry'; +import { diffDiagonalFill, diffInserted, diffRemoved, editorBackground, focusBorder, foreground } from 'vs/platform/theme/common/colorRegistry'; import { INotebookEditorWorkerService } from 'vs/workbench/contrib/notebook/common/services/notebookWorkerService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; @@ -318,5 +318,71 @@ registerThemingParticipant((theme, collector) => { } `); + const added = theme.getColor(diffInserted); + if (added) { + collector.addRule(` + .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .source-container { background-color: ${added}; } + .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .source-container .monaco-editor .margin, + .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .source-container .monaco-editor .monaco-editor-background { + background-color: ${added}; + } + ` + ); + collector.addRule(` + .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .metadata-editor-container { background-color: ${added}; } + .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .metadata-editor-container .monaco-editor .margin, + .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .metadata-editor-container .monaco-editor .monaco-editor-background { + background-color: ${added}; + } + ` + ); + collector.addRule(` + .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .output-editor-container { background-color: ${added}; } + .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .output-editor-container .monaco-editor .margin, + .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .output-editor-container .monaco-editor .monaco-editor-background { + background-color: ${added}; + } + ` + ); + collector.addRule(` + .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .metadata-header-container { background-color: ${added}; } + .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .output-header-container { background-color: ${added}; } + ` + ); + } + const removed = theme.getColor(diffRemoved); + if (added) { + collector.addRule(` + .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .source-container { background-color: ${removed}; } + .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .source-container .monaco-editor .margin, + .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .source-container .monaco-editor .monaco-editor-background { + background-color: ${removed}; + } + ` + ); + collector.addRule(` + .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .metadata-editor-container { background-color: ${removed}; } + .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .metadata-editor-container .monaco-editor .margin, + .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .metadata-editor-container .monaco-editor .monaco-editor-background { + background-color: ${removed}; + } + ` + ); + collector.addRule(` + .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .output-editor-container { background-color: ${removed}; } + .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .output-editor-container .monaco-editor .margin, + .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .output-editor-container .monaco-editor .monaco-editor-background { + background-color: ${removed}; + } + ` + ); + collector.addRule(` + .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .metadata-header-container { background-color: ${removed}; } + .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .output-header-container { background-color: ${removed}; } + ` + ); + } + + collector.addRule(`.notebook-text-diff-editor .cell-body { margin: ${DIFF_CELL_MARGIN}px; }`); }); From bd4e3c30a7c20b2f858101a5de1a1bf5af1e89b3 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 25 Aug 2020 13:05:21 -0700 Subject: [PATCH 521/736] diff view and fix compile --- .../workbench/api/common/extHostNotebook.ts | 4 +- .../notebook/browser/diff/cellComponents.ts | 4 +- .../browser/diff/notebookDiffActions.ts | 50 +++++++++++++++++++ .../browser/diff/notebookTextDiffEditor.ts | 9 +++- .../notebook/browser/notebook.contribution.ts | 3 ++ 5 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index b24e33fde16..240d9a4fb6d 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -873,7 +873,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN private static _notebookKernelProviderHandlePool: number = 0; private readonly _proxy: MainThreadNotebookShape; - private readonly _notebookContentProviders = new Map(); + private readonly _notebookContentProviders = new Map(); private readonly _notebookKernels = new Map(); private readonly _notebookKernelProviders = new Map(); private readonly _documents = new ResourceMap(); @@ -953,7 +953,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN registerNotebookContentProvider( extension: IExtensionDescription, viewType: string, - provider: vscode.NotebookContentProvider, + provider: vscode.NotebookContentProvider & { kernel?: vscode.NotebookKernel }, ): vscode.Disposable { if (this._notebookContentProviders.has(viewType)) { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index 6feb0e77731..b9259448523 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -5,14 +5,13 @@ import * as DOM from 'vs/base/browser/dom'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { Range } from 'vs/editor/common/core/range'; import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { CellDiffViewModel, PropertyFoldingState } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; import { CellDiffRenderTemplate, CellDiffViewModelLayoutChangeEvent, DIFF_CELL_MARGIN, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/common'; import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { DECORATIONS, DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; import { renderCodicons } from 'vs/base/common/codicons'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; @@ -20,7 +19,6 @@ import { format } from 'vs/base/common/jsonFormatter'; import { applyEdits } from 'vs/base/common/jsonEdit'; import { NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { hash } from 'vs/base/common/hash'; -import { Constants } from 'vs/base/common/uint'; const fixedDiffEditorOptions: IDiffEditorOptions = { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts new file mode 100644 index 00000000000..2ffd15bb0c2 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; +import { ActiveEditorContext } from 'vs/workbench/common/editor'; +import { NotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor'; +import { NotebookDiffEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookDiffEditorInput'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; + +// ActiveEditorContext.isEqualTo(SearchEditorConstants.SearchEditorID) + +registerAction2(class extends Action2 { + constructor() { + super({ + id: 'notebook.diff.switchToText', + icon: { id: 'codicon/file-code' }, + title: { value: localize('notebook.diff.switchToText', "Open Text Diff Editor"), original: 'Open Text Diff Editor' }, + precondition: ActiveEditorContext.isEqualTo(NotebookTextDiffEditor.ID), + menu: [{ + id: MenuId.EditorTitle, + group: 'navigation', + when: ActiveEditorContext.isEqualTo(NotebookTextDiffEditor.ID) + }] + }); + } + + async run(accessor: ServicesAccessor): Promise { + const editorService = accessor.get(IEditorService); + const editorGroupService = accessor.get(IEditorGroupsService); + + const activeEditor = editorService.activeEditorPane; + if (activeEditor && activeEditor instanceof NotebookTextDiffEditor) { + const leftResource = (activeEditor.input as NotebookDiffEditorInput).originalResource; + const rightResource = (activeEditor.input as NotebookDiffEditorInput).resource; + const options = { + preserveFocus: false + }; + + const label = localize('diffLeftRightLabel', "{0} ⟷ {1}", leftResource.toString(true), rightResource.toString(true)); + + await editorService.openEditor({ leftResource, rightResource, label, options }, viewColumnToEditorGroup(editorGroupService, undefined)); + } + } +}); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index 2633d1e4c3c..2f5a6554b92 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -17,7 +17,7 @@ import { WorkbenchList } from 'vs/platform/list/browser/listService'; import { CellDiffViewModel } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { CellDiffRenderer, NotebookCellTextDiffListDelegate, NotebookTextDiffList } from 'vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { diffDiagonalFill, diffInserted, diffRemoved, editorBackground, focusBorder, foreground } from 'vs/platform/theme/common/colorRegistry'; import { INotebookEditorWorkerService } from 'vs/workbench/contrib/notebook/common/services/notebookWorkerService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -31,6 +31,8 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { NotebookDiffEditorEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; +export const IN_NOTEBOOK_TEXT_DIFF_EDITOR = new RawContextKey('isInNotebookTextDiffEditor', false); + export class NotebookTextDiffEditor extends EditorPane implements INotebookTextDiffEditor { static readonly ID: string = 'workbench.editor.notebookTextDiffEditor'; @@ -42,6 +44,8 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD private readonly _onMouseUp = this._register(new Emitter<{ readonly event: MouseEvent; readonly target: CellDiffViewModel; }>()); public readonly onMouseUp = this._onMouseUp.event; private _eventDispatcher: NotebookDiffEditorEventDispatcher | undefined; + protected _scopeContextKeyService!: IContextKeyService; + private _inNotebookTextDiffEditor: IContextKey | null = null; constructor( @IInstantiationService readonly instantiationService: IInstantiationService, @@ -60,6 +64,9 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD protected createEditor(parent: HTMLElement): void { this._rootElement = DOM.append(parent, DOM.$('.notebook-text-diff-editor')); + // this._scopeContextKeyService = this._register(this.contextKeyService.createScoped(parent)); + this._inNotebookTextDiffEditor = IN_NOTEBOOK_TEXT_DIFF_EDITOR.bindTo(this.contextKeyService); + this._inNotebookTextDiffEditor.set(true); const renderer = this.instantiationService.createInstance(CellDiffRenderer, this); diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 6f0d4cba56b..80db75ecf40 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -58,6 +58,9 @@ import 'vs/workbench/contrib/notebook/browser/contrib/marker/markerProvider'; import 'vs/workbench/contrib/notebook/browser/contrib/status/editorStatus'; // import 'vs/workbench/contrib/notebook/browser/contrib/scm/scm'; +// Diff Editor Contribution +import 'vs/workbench/contrib/notebook/browser/diff/notebookDiffActions'; + // Output renderers registration import 'vs/workbench/contrib/notebook/browser/view/output/transforms/streamTransform'; From 1a16b307d4d512e2d058128f214b0880ad586f08 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 25 Aug 2020 13:36:40 -0700 Subject: [PATCH 522/736] update nb diff editor options. --- .../notebook/browser/diff/cellComponents.ts | 43 ++++++------------- .../browser/diff/notebookTextDiffEditor.ts | 9 ++++ 2 files changed, 21 insertions(+), 31 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index b9259448523..5d5f3d43fe8 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -20,36 +20,6 @@ import { applyEdits } from 'vs/base/common/jsonEdit'; import { NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { hash } from 'vs/base/common/hash'; - -const fixedDiffEditorOptions: IDiffEditorOptions = { - padding: { - top: 12, - bottom: 12 - }, - scrollBeyondLastLine: false, - scrollbar: { - verticalScrollbarSize: 14, - horizontal: 'auto', - useShadows: true, - verticalHasArrows: false, - horizontalHasArrows: false, - alwaysConsumeMouseWheel: false - }, - renderLineHighlightOnlyWhenFocus: true, - overviewRulerLanes: 0, - selectOnLineNumbers: false, - wordWrap: 'off', - lineNumbers: 'off', - lineDecorationsWidth: 0, - glyphMargin: true, - fixedOverflowWidgets: true, - minimap: { enabled: false }, - renderValidationDecorations: 'on', - enableSplitViewResizing: false, - renderIndicators: false, - renderLineHighlight: 'none' -}; - const fixedEditorOptions: IEditorOptions = { padding: { top: 12, @@ -74,9 +44,19 @@ const fixedEditorOptions: IEditorOptions = { fixedOverflowWidgets: true, minimap: { enabled: false }, renderValidationDecorations: 'on', - renderLineHighlight: 'none' + renderLineHighlight: 'none', + readOnly: true }; +const fixedDiffEditorOptions: IDiffEditorOptions = { + ...fixedEditorOptions, + glyphMargin: true, + enableSplitViewResizing: false, + renderIndicators: false, +}; + + + class PropertyHeader extends Disposable { protected _foldingIndicator!: HTMLElement; protected _statusSpan!: HTMLElement; @@ -111,6 +91,7 @@ class PropertyHeader extends Disposable { if (metadataChanged) { this._statusSpan.textContent = this.accessor.changedLabel; this._statusSpan.style.fontWeight = 'bold'; + DOM.addClass(this.metadataHeaderContainer, 'modified'); } else { this._statusSpan.textContent = this.accessor.unChangedLabel; } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index 2f5a6554b92..0e1aee2630d 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -390,6 +390,15 @@ registerThemingParticipant((theme, collector) => { ); } + // const changed = theme.getColor(editorGutterModifiedBackground); + + // if (changed) { + // collector.addRule(` + // .notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container.modified { + // background-color: ${changed}; + // } + // `); + // } collector.addRule(`.notebook-text-diff-editor .cell-body { margin: ${DIFF_CELL_MARGIN}px; }`); }); From c5134d83f73d6adab4619334c994d022e347d535 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 25 Aug 2020 15:16:57 -0700 Subject: [PATCH 523/736] Fix updating of a webview view's title --- src/vs/workbench/api/browser/mainThreadWebviewViews.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts index c882626890d..2d223830a0b 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts @@ -43,9 +43,9 @@ export class MainThreadWebviewsViews extends Disposable { this._webviewViewService.register(viewType, { resolve: async (webviewView: WebviewView, cancellation: CancellationToken) => { - this._webviewViews.set(viewType, webviewView); - const handle = webviewView.webview.id; + + this._webviewViews.set(handle, webviewView); this.mainThreadWebviews.addWebview(handle, webviewView.webview); let state = undefined; @@ -67,6 +67,7 @@ export class MainThreadWebviewsViews extends Disposable { webviewView.onDispose(() => { this._proxyViews.$disposeWebviewView(handle); + this._webviewViews.delete(handle); }); try { From 8bfdb0f87b24443e259c7c8ceacc2cfabe23f44d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Aug 2020 01:15:36 +0200 Subject: [PATCH 524/736] Improve exe based recommendations --- .../common/extensionManagement.ts | 1 + .../node/extensionTipsService.ts | 1 + .../browser/configBasedRecommendations.ts | 2 +- .../browser/exeBasedRecommendations.ts | 48 ++++++------- .../browser/extensionRecommendations.ts | 25 +++---- .../extensionRecommendationsService.ts | 7 ++ .../extensions/browser/extensionsActions.ts | 72 +++++++++++++------ .../extensions/browser/extensionsViews.ts | 19 +++++ .../browser/fileBasedRecommendations.ts | 7 +- .../common/extensionManagement.ts | 1 + 10 files changed, 111 insertions(+), 72 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 34811829b09..a572f0a2860 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -239,6 +239,7 @@ export type IExecutableBasedExtensionTip = { readonly extensionId: string, readonly extensionName: string, readonly isExtensionPack: boolean, + readonly exeName: string, readonly exeFriendlyName: string, readonly windowsPath?: string, }; diff --git a/src/vs/platform/extensionManagement/node/extensionTipsService.ts b/src/vs/platform/extensionManagement/node/extensionTipsService.ts index 5780216da37..3dd401c785b 100644 --- a/src/vs/platform/extensionManagement/node/extensionTipsService.ts +++ b/src/vs/platform/extensionManagement/node/extensionTipsService.ts @@ -105,6 +105,7 @@ export class ExtensionTipsService extends BaseExtensionTipsService { extensionId, extensionName, isExtensionPack, + exeName, exeFriendlyName: extensionTip.exeFriendlyName, windowsPath: extensionTip.windowsPath, }); diff --git a/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts index 8215282b35b..61ab9d55b35 100644 --- a/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts @@ -94,7 +94,7 @@ export class ConfigBasedRecommendations extends ExtensionRecommendations { const tip = this.importantTips.filter(tip => tip.extensionId === extension)[0]; const message = tip.isExtensionPack ? localize('extensionPackRecommended', "The '{0}' extension pack is recommended for this workspace.", tip.extensionName) : localize('extensionRecommended', "The '{0}' extension is recommended for this workspace.", tip.extensionName); - this.promptImportantExtensionsInstallNotification([extension], message); + this.promptImportantExtensionsInstallNotification([extension], message, extension, localize('more information', "More Information")); } } diff --git a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts index 60a16d6fc88..875f7833943 100644 --- a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts @@ -26,11 +26,11 @@ type ExeExtensionRecommendationsClassification = { export class ExeBasedRecommendations extends ExtensionRecommendations { - private readonly _otherRecommendations: ExtensionRecommendation[] = []; - get otherRecommendations(): ReadonlyArray { return this._otherRecommendations; } + private _otherTips: IExecutableBasedExtensionTip[] = []; + private _importantTips: IExecutableBasedExtensionTip[] = []; - private readonly _importantRecommendations: ExtensionRecommendation[] = []; - get importantRecommendations(): ReadonlyArray { return this._importantRecommendations; } + get otherRecommendations(): ReadonlyArray { return this._otherTips.map(tip => this.toExtensionRecommendation(tip)); } + get importantRecommendations(): ReadonlyArray { return this._importantTips.map(tip => this.toExtensionRecommendation(tip)); } get recommendations(): ReadonlyArray { return [...this.importantRecommendations, ...this.otherRecommendations]; } @@ -58,9 +58,20 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { timeout(3000).then(() => this.fetchAndPromptImportantExeBasedRecommendations()); } + getRecommendations(exe: string): { important: ExtensionRecommendation[], others: ExtensionRecommendation[] } { + const important = this._importantTips + .filter(tip => tip.exeName.toLowerCase() === exe.toLowerCase()) + .map(tip => this.toExtensionRecommendation(tip)); + + const others = this._otherTips + .filter(tip => tip.exeName.toLowerCase() === exe.toLowerCase()) + .map(tip => this.toExtensionRecommendation(tip)); + + return { important, others }; + } + protected async doActivate(): Promise { - const otherExectuableBasedTips = await this.extensionTipsService.getOtherExecutableBasedTips(); - otherExectuableBasedTips.forEach(tip => this._otherRecommendations.push(this.toExtensionRecommendation(tip))); + this._otherTips = await this.extensionTipsService.getOtherExecutableBasedTips(); await this.fetchImportantExeBasedRecommendations(); } @@ -74,11 +85,8 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { private async doFetchImportantExeBasedRecommendations(): Promise> { const importantExeBasedRecommendations: IStringDictionary = {}; - const importantExectuableBasedTips = await this.extensionTipsService.getImportantExecutableBasedTips(); - importantExectuableBasedTips.forEach(tip => { - this._importantRecommendations.push(this.toExtensionRecommendation(tip)); - importantExeBasedRecommendations[tip.extensionId.toLowerCase()] = tip; - }); + this._importantTips = await this.extensionTipsService.getImportantExecutableBasedTips(); + this._importantTips.forEach(tip => importantExeBasedRecommendations[tip.extensionId.toLowerCase()] = tip); return importantExeBasedRecommendations; } @@ -127,22 +135,8 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { await this.tasExperimentService.getTreatment('wslpopupaa'); } - if (tips.length === 1) { - const tip = tips[0]; - const message = tip.isExtensionPack ? localize('extensionPackRecommended', "The '{0}' extension pack is recommended as you have {1} installed on your system.", tip.extensionName, tip.exeFriendlyName || basename(tip.windowsPath!)) - : localize('exeRecommended', "The '{0}' extension is recommended as you have {1} installed on your system.", tip.extensionName, tip.exeFriendlyName || basename(tip.windowsPath!)); - this.promptImportantExtensionsInstallNotification(extensionIds, message); - } - - else if (tips.length === 2) { - const message = localize('two extensions recommended', "The '{0}' and '{1}' extensions are recommended as you have {2} installed on your system.", tips[0].extensionName, tips[1].extensionName, tips[0].exeFriendlyName || basename(tips[0].windowsPath!)); - this.promptImportantExtensionsInstallNotification(extensionIds, message); - } - - else if (tips.length > 2) { - const message = localize('more than two extensions recommended', "The '{0}', '{1}' and other extensions are recommended as you have {2} installed on your system.", tips[0].extensionName, tips[1].extensionName, tips[0].exeFriendlyName || basename(tips[0].windowsPath!)); - this.promptImportantExtensionsInstallNotification(extensionIds, message); - } + const message = localize('exeRecommended', "You have {0} installed on your system. Do you want to install recommendations for it?", tips[0].exeFriendlyName); + this.promptImportantExtensionsInstallNotification(extensionIds, message, `@exe:"${tips[0].exeName}"`); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts index 4347c796632..985a4811904 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts @@ -8,7 +8,7 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { localize } from 'vs/nls'; -import { InstallRecommendedExtensionAction, ShowRecommendedExtensionAction, ShowRecommendedExtensionsAction, InstallRecommendedExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { InstallRecommendedExtensionsAction, SearchExtensionsAction, OpenExtensionEditorAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { ExtensionRecommendationSource, IExtensionRecommendationReson } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IExtensionsConfiguration, ConfigurationKey } from 'vs/workbench/contrib/extensions/common/extensions'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; @@ -57,38 +57,33 @@ export abstract class ExtensionRecommendations extends Disposable { return this._activationPromise; } - private runAction(action: IAction) { + private async runAction(action: IAction): Promise { try { - action.run(); + await action.run(); } finally { action.dispose(); } } - protected promptImportantExtensionsInstallNotification(extensionIds: string[], message: string): void { + protected promptImportantExtensionsInstallNotification(extensionIds: string[], message: string, searchValue: string, showRecommendationsLabel?: string): void { this.notificationService.prompt(Severity.Info, message, [{ - label: extensionIds.length === 1 ? localize('install', 'Install') : localize('installAll', "Install All"), + label: localize('install', 'Install'), run: async () => { for (const extensionId of extensionIds) { this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'install', extensionId }); } - if (extensionIds.length === 1) { - this.runAction(this.instantiationService.createInstance(InstallRecommendedExtensionAction, extensionIds[0])); - } else { - this.runAction(this.instantiationService.createInstance(InstallRecommendedExtensionsAction, InstallRecommendedExtensionsAction.ID, InstallRecommendedExtensionsAction.LABEL, extensionIds, 'install-recommendations')); - } + this.runAction(this.instantiationService.createInstance(InstallRecommendedExtensionsAction, InstallRecommendedExtensionsAction.ID, InstallRecommendedExtensionsAction.LABEL, extensionIds, searchValue, 'install-recommendations')); } }, { - label: extensionIds.length === 1 ? localize('moreInformation', "More Information") : localize('showRecommendations', "Show Recommendations"), - run: () => { + label: showRecommendationsLabel || localize('show recommendations', "Show Recommendations"), + run: async () => { for (const extensionId of extensionIds) { this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'show', extensionId }); } + this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); if (extensionIds.length === 1) { - this.runAction(this.instantiationService.createInstance(ShowRecommendedExtensionAction, extensionIds[0])); - } else { - this.runAction(this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, ShowRecommendedExtensionsAction.LABEL)); + this.runAction(this.instantiationService.createInstance(OpenExtensionEditorAction, extensionIds[0])); } } }, { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts index 559f5346458..d844e46c511 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts @@ -191,6 +191,13 @@ export class ExtensionRecommendationsService extends Disposable implements IExte return this.toExtensionRecommendations(this.workspaceRecommendations.recommendations); } + async getExeBasedRecommendations(exe?: string): Promise<{ important: IExtensionRecommendation[], others: IExtensionRecommendation[] }> { + await this.exeBasedRecommendations.activate(); + const { important, others } = exe ? this.exeBasedRecommendations.getRecommendations(exe) + : { important: this.exeBasedRecommendations.importantRecommendations, others: this.exeBasedRecommendations.otherRecommendations }; + return { important: this.toExtensionRecommendations(important), others: this.toExtensionRecommendations(others) }; + } + getFileBasedRecommendations(): IExtensionRecommendation[] { return this.toExtensionRecommendations(this.fileBasedRecommendations.recommendations); } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 66df3029505..8639396bc57 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -1842,6 +1842,7 @@ export class InstallRecommendedExtensionsAction extends Action { id: string, label: string, recommendations: string[], + private readonly searchValue: string, private readonly source: string, @IViewletService private readonly viewletService: IViewletService, @IInstantiationService private readonly instantiationService: IInstantiationService, @@ -1854,22 +1855,17 @@ export class InstallRecommendedExtensionsAction extends Action { this.recommendations = recommendations; } - run(): Promise { - return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) - .then(viewlet => { - viewlet.search('@recommended '); - viewlet.focus(); - const names = this.recommendations; - return this.extensionWorkbenchService.queryGallery({ names, source: this.source }, CancellationToken.None).then(pager => { - 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 => this.installExtension(e))); - } - return Promise.all(installPromises); - }); - }); + async run(): Promise { + await new SearchExtensionsAction(this.searchValue, this.viewletService).run(); + const names = this.recommendations; + const pager = await this.extensionWorkbenchService.queryGallery({ names, source: this.source }, CancellationToken.None); + const installPromises: Promise[] = []; + const model = new PagedModel(pager); + for (let i = 0; i < pager.total; i++) { + installPromises.push(model.resolve(i, CancellationToken.None) + .then(e => this.installExtension(e))); + } + return Promise.all(installPromises); } private async installExtension(extension: IExtension): Promise { @@ -1885,6 +1881,7 @@ export class InstallRecommendedExtensionsAction extends Action { return; } } + this.extensionWorkbenchService.open(extension, { pinned: true }); await this.extensionWorkbenchService.install(extension); } catch (err) { console.error(err); @@ -1904,7 +1901,7 @@ export class InstallWorkspaceRecommendedExtensionsAction extends InstallRecommen @IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService, @IProductService productService: IProductService, ) { - super('workbench.extensions.action.installWorkspaceRecommendedExtensions', localize('installWorkspaceRecommendedExtensions', "Install Workspace Recommended Extensions"), recommendations, 'install-all-workspace-recommendations', + super('workbench.extensions.action.installWorkspaceRecommendedExtensions', localize('installWorkspaceRecommendedExtensions', "Install Workspace Recommended Extensions"), recommendations, '@recommended ', 'install-all-workspace-recommendations', viewletService, instantiationService, extensionWorkbenchService, configurationService, extensionManagementServerService, productService); } } @@ -1943,6 +1940,24 @@ export class ShowRecommendedExtensionAction extends Action { } } +export class OpenExtensionEditorAction extends Action { + + constructor( + private readonly extensionId: string, + @IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService, + ) { + super('extensions.openExtension', localize('open extension', "Open Extension"), undefined, true); + } + + async run(): Promise { + const pager = await this.extensionWorkbenchService.queryGallery({ names: [this.extensionId], source: 'install-recommendation', pageSize: 1 }, CancellationToken.None); + if (pager && pager.firstPage && pager.firstPage.length) { + const extension = pager.firstPage[0]; + return this.extensionWorkbenchService.open(extension); + } + } +} + export class InstallRecommendedExtensionAction extends Action { static readonly ID = 'workbench.extensions.action.installRecommendedExtension'; @@ -2087,12 +2102,23 @@ export class SearchCategoryAction extends Action { } run(): Promise { - return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) - .then(viewlet => { - viewlet.search(`@category:"${this.category.toLowerCase()}"`); - viewlet.focus(); - }); + return new SearchExtensionsAction(`@category:"${this.category.toLowerCase()}"`, this.viewletService).run(); + } +} + +export class SearchExtensionsAction extends Action { + + constructor( + private readonly searchValue: string, + @IViewletService private readonly viewletService: IViewletService + ) { + super('extensions.searchExtensions', localize('search recommendations', "Search Extensions"), undefined, true); + } + + async run(): Promise { + const viewPaneContainer = (await this.viewletService.openViewlet(VIEWLET_ID, true))?.getViewPaneContainer() as IExtensionsViewPaneContainer; + viewPaneContainer.search(this.searchValue); + viewPaneContainer.focus(); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 8e34bdaf743..d7f579aad69 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -460,6 +460,8 @@ export class ExtensionsListView extends ViewPane { return this.getWorkspaceRecommendationsModel(query, options, token); } else if (ExtensionsListView.isKeymapsRecommendedExtensionsQuery(query.value)) { return this.getKeymapRecommendationsModel(query, options, token); + } else if (ExtensionsListView.isExeRecommendedExtensionsQuery(query.value)) { + return this.getExeRecommendationsModel(query, options, token); } else if (/@recommended:all/i.test(query.value) || ExtensionsListView.isSearchRecommendedExtensionsQuery(query.value)) { return this.getAllRecommendationsModel(query, options, token); } else if (ExtensionsListView.isRecommendedExtensionsQuery(query.value)) { @@ -732,6 +734,19 @@ export class ExtensionsListView extends ViewPane { .then(result => this.getPagedModel(result)); } + private async getExeRecommendationsModel(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { + const exe = query.value.replace(/@exe:/g, '').trim().toLowerCase(); + const { important } = await this.tipsService.getExeBasedRecommendations(exe.startsWith('"') ? exe.substring(1, exe.length - 1) : exe); + const names: string[] = important.map(({ extensionId }) => extensionId); + + if (!names.length) { + return Promise.resolve(new PagedModel([])); + } + options.source = 'recommendations-exe'; + return this.extensionsWorkbenchService.queryGallery(assign(options, { names, pageSize: names.length }), token) + .then(result => this.getPagedModel(result)); + } + // Sorts the firstPage of the pager in the same order as given array of extension ids private sortFirstPage(pager: IPager, ids: string[]) { ids = ids.map(x => x.toLowerCase()); @@ -864,6 +879,10 @@ export class ExtensionsListView extends ViewPane { return /@recommended:workspace/i.test(query); } + static isExeRecommendedExtensionsQuery(query: string): boolean { + return /@exe:.+/i.test(query); + } + static isKeymapsRecommendedExtensionsQuery(query: string): boolean { return /@recommended:keymaps/i.test(query); } diff --git a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts index fcc7c701ba1..2e95bd74871 100644 --- a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts @@ -226,13 +226,8 @@ export class FileBasedRecommendations extends ExtensionRecommendations { if (!entry) { return false; } - const extensionName = entry.name; - let message = localize('reallyRecommended2', "The '{0}' extension is recommended for this file type.", extensionName); - if (entry.isExtensionPack) { - message = localize('reallyRecommendedExtensionPack', "The '{0}' extension pack is recommended for this file type.", extensionName); - } - this.promptImportantExtensionsInstallNotification([extensionId], message); + this.promptImportantExtensionsInstallNotification([extensionId], localize('reallyRecommended', "Do you want to install recommendations for this file?"), extensionId); return true; } diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts index 34647803e9b..12c949d67d2 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts @@ -128,6 +128,7 @@ export interface IExtensionRecommendationsService { getAllRecommendationsWithReason(): IStringDictionary; getFileBasedRecommendations(): IExtensionRecommendation[]; + getExeBasedRecommendations(exe?: string): Promise<{ important: IExtensionRecommendation[], others: IExtensionRecommendation[] }>; getImportantRecommendations(): Promise; getConfigBasedRecommendations(): Promise; getOtherRecommendations(): Promise; From ee1a39e1eeb252f517bc26cfbc506b97093eca38 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 25 Aug 2020 16:19:19 -0700 Subject: [PATCH 525/736] Fix sizing cell incorrectly for webview outputs - webview outputs are not "hasDynamicHeight" - The "else if" condition got flipped around in the last change --- .../browser/view/renderers/codeCell.ts | 66 +++++++++---------- .../contrib/notebook/common/notebookCommon.ts | 2 +- 2 files changed, 33 insertions(+), 35 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index 0a305a7d9d5..97e8d538e04 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -325,7 +325,7 @@ export class CodeCell extends Disposable { if (renderedOutput.renderResult.type !== RenderOutputType.None) { // Show inset in webview, or render output that isn't rendered // TODO@roblou skipHeightInit flag is a hack - the webview only sends the real height once. Don't wipe it out here. - this.renderOutput(currOutput, index, undefined, true); + this.renderOutput(currOutput, index, undefined); } else { // Anything else, just update the height this.viewCell.updateOutputHeight(index, renderedOutput.element.clientHeight); @@ -430,7 +430,7 @@ export class CodeCell extends Disposable { ); } - private renderOutput(currOutput: IProcessedOutput, index: number, beforeElement?: HTMLElement, skipHeightInit = false) { + private renderOutput(currOutput: IProcessedOutput, index: number, beforeElement?: HTMLElement) { if (!this.outputResizeListeners.has(currOutput)) { this.outputResizeListeners.set(currOutput, new DisposableStore()); } @@ -510,42 +510,40 @@ export class CodeCell extends Disposable { outputItemDiv.style.position = 'absolute'; } - if (!skipHeightInit) { - if (outputHasDynamicHeight(result)) { - this.viewCell.selfSizeMonitoring = true; + if (outputHasDynamicHeight(result)) { + this.viewCell.selfSizeMonitoring = true; - const clientHeight = outputItemDiv.clientHeight; - const dimension = { - width: this.viewCell.layoutInfo.editorWidth, - height: clientHeight - }; - const elementSizeObserver = getResizesObserver(outputItemDiv, dimension, () => { - if (this.templateData.outputContainer && document.body.contains(this.templateData.outputContainer!)) { - const height = Math.ceil(elementSizeObserver.getHeight()); + const clientHeight = outputItemDiv.clientHeight; + const dimension = { + width: this.viewCell.layoutInfo.editorWidth, + height: clientHeight + }; + const elementSizeObserver = getResizesObserver(outputItemDiv, dimension, () => { + if (this.templateData.outputContainer && document.body.contains(this.templateData.outputContainer!)) { + const height = Math.ceil(elementSizeObserver.getHeight()); - if (clientHeight === height) { - return; - } - - const currIndex = this.viewCell.outputs.indexOf(currOutput); - if (currIndex < 0) { - return; - } - - this.viewCell.updateOutputHeight(currIndex, height); - this.relayoutCell(); + if (clientHeight === height) { + return; } - }); - elementSizeObserver.startObserving(); - this.outputResizeListeners.get(currOutput)!.add(elementSizeObserver); - this.viewCell.updateOutputHeight(index, clientHeight); - } else if (result.type !== RenderOutputType.None) { // no-op if it's a webview - const clientHeight = Math.ceil(outputItemDiv.clientHeight); - this.viewCell.updateOutputHeight(index, clientHeight); - const top = this.viewCell.getOutputOffsetInContainer(index); - outputItemDiv.style.top = `${top}px`; - } + const currIndex = this.viewCell.outputs.indexOf(currOutput); + if (currIndex < 0) { + return; + } + + this.viewCell.updateOutputHeight(currIndex, height); + this.relayoutCell(); + } + }); + elementSizeObserver.startObserving(); + this.outputResizeListeners.get(currOutput)!.add(elementSizeObserver); + this.viewCell.updateOutputHeight(index, clientHeight); + } else if (result.type === RenderOutputType.None) { // no-op if it's a webview + const clientHeight = Math.ceil(outputItemDiv.clientHeight); + this.viewCell.updateOutputHeight(index, clientHeight); + + const top = this.viewCell.getOutputOffsetInContainer(index); + outputItemDiv.style.top = `${top}px`; } } diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 2ad327e4472..52520303aa6 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -313,7 +313,7 @@ export interface IRenderOutputViaExtension { export type IInsetRenderOutput = IRenderPlainHtmlOutput | IRenderOutputViaExtension; export type IRenderOutput = IRenderNoOutput | IInsetRenderOutput; -export const outputHasDynamicHeight = (o: IRenderOutput) => o.type === RenderOutputType.Extension || o.hasDynamicHeight; +export const outputHasDynamicHeight = (o: IRenderOutput) => o.type !== RenderOutputType.Extension && o.hasDynamicHeight; export type NotebookCellTextModelSplice = [ number /* start */, From 0f22246a4482062668e93c8387eb47560fb95443 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Aug 2020 01:45:18 +0200 Subject: [PATCH 526/736] improve wordings and show all recommendations --- .../contrib/extensions/browser/exeBasedRecommendations.ts | 2 +- .../workbench/contrib/extensions/browser/extensionsViews.ts | 4 ++-- .../contrib/extensions/browser/fileBasedRecommendations.ts | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts index 875f7833943..cd0fc7eae4d 100644 --- a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts @@ -135,7 +135,7 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { await this.tasExperimentService.getTreatment('wslpopupaa'); } - const message = localize('exeRecommended', "You have {0} installed on your system. Do you want to install recommendations for it?", tips[0].exeFriendlyName); + const message = localize('exeRecommended', "You have {0} installed on your system. Do you want to install support for it?", tips[0].exeFriendlyName); this.promptImportantExtensionsInstallNotification(extensionIds, message, `@exe:"${tips[0].exeName}"`); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index d7f579aad69..3cc77454f2b 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -736,8 +736,8 @@ export class ExtensionsListView extends ViewPane { private async getExeRecommendationsModel(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { const exe = query.value.replace(/@exe:/g, '').trim().toLowerCase(); - const { important } = await this.tipsService.getExeBasedRecommendations(exe.startsWith('"') ? exe.substring(1, exe.length - 1) : exe); - const names: string[] = important.map(({ extensionId }) => extensionId); + const { important, others } = await this.tipsService.getExeBasedRecommendations(exe.startsWith('"') ? exe.substring(1, exe.length - 1) : exe); + const names: string[] = [...important, ...others].map(({ extensionId }) => extensionId); if (!names.length) { return Promise.resolve(new PagedModel([])); diff --git a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts index 2e95bd74871..15653906611 100644 --- a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts @@ -189,7 +189,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { } const installed = await this.extensionsWorkbenchService.queryLocal(); - if (await this.promptRecommendedExtensionForFileType(recommendationsToPrompt, installed)) { + if (await this.promptRecommendedExtensionForFileType(fileExtension.substring(1), recommendationsToPrompt, installed)) { return; } @@ -209,7 +209,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { this.promptRecommendedExtensionForFileExtension(fileExtension, installed); } - private async promptRecommendedExtensionForFileType(recommendations: string[], installed: IExtension[]): Promise { + private async promptRecommendedExtensionForFileType(ext: string, recommendations: string[], installed: IExtension[]): Promise { recommendations = this.filterIgnoredOrNotAllowed(recommendations); if (recommendations.length === 0) { @@ -227,7 +227,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { return false; } - this.promptImportantExtensionsInstallNotification([extensionId], localize('reallyRecommended', "Do you want to install recommendations for this file?"), extensionId); + this.promptImportantExtensionsInstallNotification([extensionId], localize('reallyRecommended', "Do you want to install support for this file?"), `ext:${ext}`); return true; } From 804d5cbfe375106d7c60976f12bf8392844dd4b4 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 25 Aug 2020 20:34:57 -0700 Subject: [PATCH 527/736] Run one inset modification at a time so they don't trample each other Fix #105320 --- .../contrib/notebook/browser/view/renderers/codeCell.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index 97e8d538e04..480e928baad 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -32,6 +32,9 @@ interface IRenderedOutput { export class CodeCell extends Disposable { private outputResizeListeners = new Map(); private outputElements = new Map(); + + private modifyInsetQueue = Promise.resolve(); + constructor( private notebookEditor: INotebookEditor, private viewCell: CodeCellViewModel, @@ -170,7 +173,7 @@ export class CodeCell extends Disposable { removedKeys.push(key); // remove element from DOM this.templateData?.outputContainer?.removeChild(value.element); - this.notebookEditor.removeInset(key); + this.modifyInsetQueue = this.modifyInsetQueue.finally(() => this.notebookEditor.removeInset(key)); } }); @@ -503,7 +506,7 @@ export class CodeCell extends Disposable { if (result.type !== RenderOutputType.None) { this.viewCell.selfSizeMonitoring = true; - this.notebookEditor.createInset(this.viewCell, result, this.viewCell.getOutputOffset(index)); + this.modifyInsetQueue = this.modifyInsetQueue.finally(() => this.notebookEditor.createInset(this.viewCell, result as any, this.viewCell.getOutputOffset(index))); } else { DOM.addClass(outputItemDiv, 'foreground'); DOM.addClass(outputItemDiv, 'output-element'); @@ -598,7 +601,7 @@ export class CodeCell extends Disposable { const element = this.outputElements.get(output)?.element; if (element) { this.templateData?.outputContainer?.removeChild(element); - this.notebookEditor.removeInset(output); + await (this.modifyInsetQueue = this.modifyInsetQueue.finally(() => this.notebookEditor.removeInset(output))); } output.pickedMimeTypeIndex = pick; From 46860a105b2f2e115970ad0d54f5a39d5add9cc5 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 26 Aug 2020 08:32:19 +0200 Subject: [PATCH 528/736] sandbox - allow to bring up a minimal workbench with simple services and in-memory file-system --- src/vs/code/electron-main/app.ts | 8 +- src/vs/code/electron-main/window.ts | 24 +- .../electron-sandbox/workbench/workbench.html | 18 + .../electron-sandbox/workbench/workbench.js | 74 ++ src/vs/platform/storage/common/storage.ts | 2 + .../browser/extensions.contribution.ts | 3 +- .../browser/extensions.web.contribution.ts | 11 + .../extensions.contribution.ts | 3 + .../electron-sandbox/desktop.main.ts | 201 ++++ .../sandbox.simpleservices.ts | 906 ++++++++++++++++++ .../workbench.desktop.sandbox.main.ts | 43 + src/vs/workbench/workbench.web.main.ts | 3 + 12 files changed, 1290 insertions(+), 6 deletions(-) create mode 100644 src/vs/code/electron-sandbox/workbench/workbench.html create mode 100644 src/vs/code/electron-sandbox/workbench/workbench.js create mode 100644 src/vs/workbench/contrib/extensions/browser/extensions.web.contribution.ts create mode 100644 src/vs/workbench/electron-sandbox/desktop.main.ts create mode 100644 src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts create mode 100644 src/vs/workbench/workbench.desktop.sandbox.main.ts diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 21a4eeb08e0..384586f6f41 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -50,7 +50,7 @@ import { setUnexpectedErrorHandler, onUnexpectedError } from 'vs/base/common/err import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener'; import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver'; import { IMenubarMainService, MenubarMainService } from 'vs/platform/menubar/electron-main/menubarMainService'; -import { RunOnceScheduler } from 'vs/base/common/async'; +import { RunOnceScheduler, timeout } from 'vs/base/common/async'; import { registerContextMenuListener } from 'vs/base/parts/contextmenu/electron-main/contextmenu'; import { homedir } from 'os'; import { join, sep, posix } from 'vs/base/common/path'; @@ -272,6 +272,12 @@ export class CodeApplication extends Disposable { try { const shellEnv = await getShellEnvironment(this.logService, this.environmentService); + + // TODO@sandbox workaround for https://github.com/electron/electron/issues/25119 + if (this.environmentService.sandbox) { + await timeout(100); + } + if (!webContents.isDestroyed()) { webContents.send('vscode:acceptShellEnv', shellEnv); } diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index c300a83ac0e..bf55feac2e5 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -167,12 +167,23 @@ export class CodeWindow extends Disposable implements ICodeWindow { title: product.nameLong, webPreferences: { preload: URI.parse(this.doGetPreloadUrl()).fsPath, - nodeIntegration: true, enableWebSQL: false, enableRemoteModule: false, nativeWindowOpen: true, webviewTag: true, - zoomFactor: zoomLevelToZoomFactor(windowConfig?.zoomLevel) + zoomFactor: zoomLevelToZoomFactor(windowConfig?.zoomLevel), + ...this.environmentService.sandbox ? + + // Sandbox + { + sandbox: true, + contextIsolation: true + } : + + // No Sandbox + { + nodeIntegration: true + } } }; @@ -799,7 +810,14 @@ export class CodeWindow extends Disposable implements ICodeWindow { } private doGetUrl(config: object): string { - return `${require.toUrl('vs/code/electron-browser/workbench/workbench.html')}?config=${encodeURIComponent(JSON.stringify(config))}`; + let workbench: string; + if (this.environmentService.sandbox) { + workbench = 'vs/code/electron-sandbox/workbench/workbench.html'; + } else { + workbench = 'vs/code/electron-browser/workbench/workbench.html'; + } + + return `${require.toUrl(workbench)}?config=${encodeURIComponent(JSON.stringify(config))}`; } private doGetPreloadUrl(): string { diff --git a/src/vs/code/electron-sandbox/workbench/workbench.html b/src/vs/code/electron-sandbox/workbench/workbench.html new file mode 100644 index 00000000000..40737461d29 --- /dev/null +++ b/src/vs/code/electron-sandbox/workbench/workbench.html @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/vs/code/electron-sandbox/workbench/workbench.js b/src/vs/code/electron-sandbox/workbench/workbench.js new file mode 100644 index 00000000000..bac5dd6d6e8 --- /dev/null +++ b/src/vs/code/electron-sandbox/workbench/workbench.js @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// + +//@ts-check +'use strict'; + +const perf = (function () { + globalThis.MonacoPerformanceMarks = globalThis.MonacoPerformanceMarks || []; + return { + /** + * @param {string} name + */ + mark(name) { + globalThis.MonacoPerformanceMarks.push(name, Date.now()); + } + }; +})(); + +perf.mark('renderer/started'); + +/** + * @type {{ + * load: (modules: string[], resultCallback: (result, configuration: object) => any, options: object) => unknown, + * globals: () => typeof import('../../../base/parts/sandbox/electron-sandbox/globals') + * }} + */ +const bootstrapWindow = (() => { + // @ts-ignore (defined in bootstrap-window.js) + return window.MonacoBootstrapWindow; +})(); + +// Load environment in parallel to workbench loading to avoid waterfall +const whenEnvResolved = bootstrapWindow.globals().process.whenEnvResolved; + +// Load workbench main JS, CSS and NLS all in parallel. This is an +// optimization to prevent a waterfall of loading to happen, because +// we know for a fact that workbench.desktop.sandbox.main will depend on +// the related CSS and NLS counterparts. +bootstrapWindow.load([ + 'vs/workbench/workbench.desktop.sandbox.main', + 'vs/nls!vs/workbench/workbench.desktop.main', + 'vs/css!vs/workbench/workbench.desktop.main' +], + async function (workbench, configuration) { + + // Mark start of workbench + perf.mark('didLoadWorkbenchMain'); + performance.mark('workbench-start'); + + // Wait for process environment being fully resolved + await whenEnvResolved; + + perf.mark('main/startup'); + + // @ts-ignore + return require('vs/workbench/electron-sandbox/desktop.main').main(configuration); + }, + { + removeDeveloperKeybindingsAfterLoad: true, + canModifyDOM: function (windowConfig) { + // TODO@sandbox part-splash is non-sandboxed only + }, + beforeLoaderConfig: function (windowConfig, loaderConfig) { + loaderConfig.recordStats = true; + }, + beforeRequire: function () { + perf.mark('willLoadWorkbenchMain'); + } + } +); diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index 52f9d10757a..6611f1dae42 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -240,6 +240,8 @@ export class InMemoryStorageService extends Disposable implements IStorageServic isNew(): boolean { return true; // always new when in-memory } + + async close(): Promise { } } export async function logStorage(global: Map, workspace: Map, globalPath: string, workspacePath: string): Promise { diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 4665a96c74e..5287eea642b 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -15,7 +15,6 @@ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWo import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/services/output/common/output'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID } from 'vs/workbench/contrib/extensions/common/extensions'; -import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'; import { OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction, ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, ShowBuiltInExtensionsAction, UpdateAllAction, @@ -55,7 +54,7 @@ import { MultiCommand } from 'vs/editor/browser/editorExtensions'; import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; // Singletons -registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService); +// registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService); // TODO@sandbox TODO@ben uncomment when 'semver-umd' can be loaded registerSingleton(IExtensionRecommendationsService, ExtensionRecommendationsService); Registry.as(OutputExtensions.OutputChannels) diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.web.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.web.contribution.ts new file mode 100644 index 00000000000..619906cc521 --- /dev/null +++ b/src/vs/workbench/contrib/extensions/browser/extensions.web.contribution.ts @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; +import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'; + +// TODO@sandbox TODO@ben move back into common/extensions.contribution.ts when 'semver-umd' can be loaded +registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService); diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts index 6d0222c798e..4a098ffd88f 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts @@ -24,8 +24,11 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; import { OpenExtensionsFolderAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions'; import { ExtensionsLabel } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'; +import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; // Singletons +registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService); // TODO@sandbox TODO@ben move back into common/extensions.contribution.ts when 'semver-umd' can be loaded registerSingleton(IExtensionHostProfileService, ExtensionHostProfileService, true); const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts new file mode 100644 index 00000000000..9b4c13f9441 --- /dev/null +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -0,0 +1,201 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { zoomLevelToZoomFactor } from 'vs/platform/windows/common/windows'; +import { importEntries, mark } from 'vs/base/common/performance'; +import { Workbench } from 'vs/workbench/browser/workbench'; +import { setZoomLevel, setZoomFactor, setFullscreen } from 'vs/base/browser/browser'; +import { domContentLoaded, addDisposableListener, EventType, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; +import { URI } from 'vs/base/common/uri'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { ILogService } from 'vs/platform/log/common/log'; +import { Schemas } from 'vs/base/common/network'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IMainProcessService, MainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; +import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { FileService } from 'vs/platform/files/common/fileService'; +import { IFileService } from 'vs/platform/files/common/files'; +import { RemoteFileSystemProvider } from 'vs/workbench/services/remote/common/remoteAgentFileSystemChannel'; +import { ISignService } from 'vs/platform/sign/common/sign'; +import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; +import { IProductService } from 'vs/platform/product/common/productService'; +import product from 'vs/platform/product/common/product'; +import { IResourceIdentityService } from 'vs/platform/resource/common/resourceIdentityService'; +import { IElectronService, ElectronService } from 'vs/platform/electron/electron-sandbox/electron'; +import { SimpleConfigurationService, simpleFileSystemProvider, SimpleLogService, SimpleRemoteAgentService, SimpleRemoteAuthorityResolverService, SimpleResourceIdentityService, SimpleSignService, SimpleStorageService, SimpleWorkspaceService } from 'vs/workbench/electron-sandbox/sandbox.simpleservices'; +import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; + +class DesktopMain extends Disposable { + + private readonly environmentService = new BrowserWorkbenchEnvironmentService({ + logsPath: URI.file('logs-path'), + workspaceId: '' + }); + + constructor(private configuration: any /*INativeWindowConfiguration*/) { + super(); + + this.init(); + } + + private init(): void { + + // Setup perf + importEntries(this.configuration.perfEntries); + + // Browser config + const zoomLevel = this.configuration.zoomLevel || 0; + setZoomFactor(zoomLevelToZoomFactor(zoomLevel)); + setZoomLevel(zoomLevel, true /* isTrusted */); + setFullscreen(!!this.configuration.fullscreen); + } + + async open(): Promise { + const services = await this.initServices(); + + await domContentLoaded(); + mark('willStartWorkbench'); + + // Create Workbench + const workbench = new Workbench(document.body, services.serviceCollection, services.logService); + + // Listeners + this.registerListeners(workbench, services.storageService); + + // Startup + workbench.startup(); + + // Logging + services.logService.trace('workbench configuration', JSON.stringify(this.environmentService.configuration)); + } + + private registerListeners(workbench: Workbench, storageService: SimpleStorageService): void { + + // Layout + this._register(addDisposableListener(window, EventType.RESIZE, e => this.onWindowResize(e, true, workbench))); + + // Workbench Lifecycle + this._register(workbench.onShutdown(() => this.dispose())); + this._register(workbench.onWillShutdown(event => event.join(storageService.close()))); + } + + private onWindowResize(e: Event, retry: boolean, workbench: Workbench): void { + if (e.target === window) { + if (window.document && window.document.body && window.document.body.clientWidth === 0) { + // TODO@Ben this is an electron issue on macOS when simple fullscreen is enabled + // where for some reason the window clientWidth is reported as 0 when switching + // between simple fullscreen and normal screen. In that case we schedule the layout + // call at the next animation frame once, in the hope that the dimensions are + // proper then. + if (retry) { + scheduleAtNextAnimationFrame(() => this.onWindowResize(e, false, workbench)); + } + return; + } + + workbench.layout(); + } + } + + private async initServices(): Promise<{ serviceCollection: ServiceCollection, logService: ILogService, storageService: SimpleStorageService }> { + const serviceCollection = new ServiceCollection(); + + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // NOTE: DO NOT ADD ANY OTHER SERVICE INTO THE COLLECTION HERE. + // CONTRIBUTE IT VIA WORKBENCH.DESKTOP.MAIN.TS AND registerSingleton(). + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + // Main Process + const mainProcessService = this._register(new MainProcessService(this.configuration.windowId)); + serviceCollection.set(IMainProcessService, mainProcessService); + + // Environment + serviceCollection.set(IWorkbenchEnvironmentService, this.environmentService); + + // Product + const productService: IProductService = { _serviceBrand: undefined, ...product }; + serviceCollection.set(IProductService, productService); + + // Log + const logService = new SimpleLogService(); + serviceCollection.set(ILogService, logService); + + // Remote + const remoteAuthorityResolverService = new SimpleRemoteAuthorityResolverService(); + serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService); + + // Sign + const signService = new SimpleSignService(); + serviceCollection.set(ISignService, signService); + + // Remote Agent + const remoteAgentService = new SimpleRemoteAgentService(); + serviceCollection.set(IRemoteAgentService, remoteAgentService); + + // Electron + const electronService = new ElectronService(this.configuration.windowId, mainProcessService) as IElectronService; + serviceCollection.set(IElectronService, electronService); + + // Files + const fileService = this._register(new FileService(logService)); + serviceCollection.set(IFileService, fileService); + + fileService.registerProvider(Schemas.file, simpleFileSystemProvider); + + // User Data Provider + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(URI.file('user-home'), this.environmentService.backupHome, simpleFileSystemProvider, this.environmentService, logService)); + + const connection = remoteAgentService.getConnection(); + if (connection) { + const remoteFileSystemProvider = this._register(new RemoteFileSystemProvider(remoteAgentService)); + fileService.registerProvider(Schemas.vscodeRemote, remoteFileSystemProvider); + } + + const resourceIdentityService = new SimpleResourceIdentityService(); + serviceCollection.set(IResourceIdentityService, resourceIdentityService); + + const services = await Promise.all([ + this.createWorkspaceService().then(service => { + + // Workspace + serviceCollection.set(IWorkspaceContextService, service); + + // Configuration + serviceCollection.set(IConfigurationService, new SimpleConfigurationService()); + + return service; + }), + + this.createStorageService().then(service => { + + // Storage + serviceCollection.set(IStorageService, service); + + return service; + }) + ]); + + return { serviceCollection, logService, storageService: services[1] }; + } + + private async createWorkspaceService(): Promise { + return new SimpleWorkspaceService(); + } + + private async createStorageService(): Promise { + return new SimpleStorageService(); + } +} + +export function main(configuration: any /*INativeWindowConfiguration*/): Promise { + const workbench = new DesktopMain(configuration); + + return workbench.open(); +} diff --git a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts new file mode 100644 index 00000000000..c8b70e5f28d --- /dev/null +++ b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts @@ -0,0 +1,906 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* eslint-disable code-no-standalone-editor */ +/* eslint-disable code-import-patterns */ + +import { ConsoleLogService } from 'vs/platform/log/common/log'; +import { IResourceIdentityService } from 'vs/platform/resource/common/resourceIdentityService'; +import { ISignService } from 'vs/platform/sign/common/sign'; +import { hash } from 'vs/base/common/hash'; +import { URI } from 'vs/base/common/uri'; +import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; +import { IRemoteAuthorityResolverService, IRemoteConnectionData, ResolvedAuthority, ResolvedOptions, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { Event } from 'vs/base/common/event'; +import { IRemoteAgentConnection, IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics'; +import { IAddressProvider, ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection'; +import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; +import { ITelemetryData, ITelemetryInfo, ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { BrowserSocketFactory } from 'vs/platform/remote/browser/browserSocketFactory'; +import { ExtensionIdentifier, ExtensionType, IExtension, IExtensionDescription, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; +import { SimpleConfigurationService as BaseSimpleConfigurationService } from 'vs/editor/standalone/browser/simpleServices'; +import { InMemoryStorageService } from 'vs/platform/storage/common/storage'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup'; +import { ITextSnapshot } from 'vs/editor/common/model'; +import { IExtensionService, NullExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { ClassifiedEvent, GDPRClassification, StrictPropertyChecker } from 'vs/platform/telemetry/common/gdprTypings'; +import { IKeyboardLayoutInfo, IKeymapService, ILinuxKeyboardLayoutInfo, ILinuxKeyboardMapping, IMacKeyboardLayoutInfo, IMacKeyboardMapping, IWindowsKeyboardLayoutInfo, IWindowsKeyboardMapping } from 'vs/workbench/services/keybinding/common/keymapInfo'; +import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; +import { DispatchConfig } from 'vs/workbench/services/keybinding/common/dispatchConfig'; +import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; +import { ChordKeybinding, ResolvedKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes'; +import { ScanCodeBinding } from 'vs/base/common/scanCode'; +import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; +import { isWindows, OperatingSystem, OS } from 'vs/base/common/platform'; +import { IPathService } from 'vs/workbench/services/path/common/pathService'; +import { posix, win32 } from 'vs/base/common/path'; +import { IConfirmation, IConfirmationResult, IDialogOptions, IDialogService, IShowResult } from 'vs/platform/dialogs/common/dialogs'; +import Severity from 'vs/base/common/severity'; +import { IWebviewService, WebviewContentOptions, WebviewElement, WebviewExtensionDescription, WebviewIcons, WebviewOptions, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { AbstractTextFileService } from 'vs/workbench/services/textfile/browser/textFileService'; +import { EnablementState, ExtensionRecommendationReason, IExtensionManagementServer, IExtensionManagementServerService, IExtensionRecommendation } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { LanguageId, TokenizationRegistry } from 'vs/editor/common/modes'; +import { IGrammar, ITextMateService } from 'vs/workbench/services/textMate/common/textMateService'; +import { AccessibilitySupport, IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { ITunnelProvider, ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { IManualSyncTask, IResourcePreview, ISyncResourceHandle, ISyncTask, IUserDataAutoSyncService, IUserDataSyncService, IUserDataSyncStore, IUserDataSyncStoreManagementService, SyncResource, SyncStatus, UserDataSyncStoreType } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncAccount, IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; +import { AbstractTimerService, IStartupMetrics, ITimerService, Writeable } from 'vs/workbench/services/timer/browser/timerService'; +import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; +import { ISingleFolderWorkspaceIdentifier, IWorkspaceFolderCreationData, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { ITaskProvider, ITaskService, ITaskSummary, ProblemMatcherRunOptions, Task, TaskFilter, TaskTerminateResponse, WorkspaceFolderTaskResult } from 'vs/workbench/contrib/tasks/common/taskService'; +import { Action } from 'vs/base/common/actions'; +import { LinkedMap } from 'vs/base/common/map'; +import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder, WorkbenchState, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { CustomTask, ContributedTask, InMemoryTask, TaskRunSource, ConfiguringTask, TaskIdentifier, TaskSorter } from 'vs/workbench/contrib/tasks/common/tasks'; +import { TaskSystemInfo } from 'vs/workbench/contrib/tasks/common/taskSystem'; +import { IExtensionManagementService, ILocalExtension, IGalleryExtension, IReportedExtension, IGalleryMetadata, IExtensionIdentifier, IExtensionTipsService, IConfigBasedExtensionTip, IExecutableBasedExtensionTip, IWorkspaceTips } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IWorkspaceTagsService, Tags } from 'vs/workbench/contrib/tags/common/workspaceTags'; +import { AsbtractOutputChannelModelService, IOutputChannelModelService } from 'vs/workbench/services/output/common/outputChannelModel'; +import { Color, RGBA } from 'vs/base/common/color'; +import { joinPath } from 'vs/base/common/resources'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; + +//#region Workspace + +export const workspaceResource = URI.file(isWindows ? 'C:\\simpleWorkspace' : '/simpleWorkspace'); + +export class SimpleWorkspaceService implements IWorkspaceContextService { + + declare readonly _serviceBrand: undefined; + + readonly onDidChangeWorkspaceName = Event.None; + readonly onDidChangeWorkspaceFolders = Event.None; + readonly onDidChangeWorkbenchState = Event.None; + + private readonly workspace: IWorkspace; + + constructor() { + this.workspace = { id: '4064f6ec-cb38-4ad0-af64-ee6467e63c82', folders: [new WorkspaceFolder({ uri: workspaceResource, name: '', index: 0 })] }; + } + + async getCompleteWorkspace(): Promise { return this.getWorkspace(); } + + getWorkspace(): IWorkspace { return this.workspace; } + + getWorkbenchState(): WorkbenchState { + if (this.workspace) { + if (this.workspace.configuration) { + return WorkbenchState.WORKSPACE; + } + return WorkbenchState.FOLDER; + } + return WorkbenchState.EMPTY; + } + + getWorkspaceFolder(resource: URI): IWorkspaceFolder | null { return resource && resource.scheme === workspaceResource.scheme ? this.workspace.folders[0] : null; } + isInsideWorkspace(resource: URI): boolean { return resource && resource.scheme === workspaceResource.scheme; } + isCurrentWorkspace(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): boolean { return true; } +} + +//#endregion + + +//#region Configuration + +export class SimpleStorageService extends InMemoryStorageService { } + +//#endregion + + +//#region Configuration + +export class SimpleConfigurationService extends BaseSimpleConfigurationService { } + +//#endregion + + +//#region Logger + +export class SimpleLogService extends ConsoleLogService { } + +export class SimpleSignService implements ISignService { + + declare readonly _serviceBrand: undefined; + + async sign(value: string): Promise { return value; } +} + +//#endregion + + +//#region Files + +class SimpleFileSystemProvider extends InMemoryFileSystemProvider { } + +export const simpleFileSystemProvider = new SimpleFileSystemProvider(); + +function createFile(parent: string, name: string, content: string = ''): void { + simpleFileSystemProvider.writeFile(joinPath(workspaceResource, parent, name), VSBuffer.fromString(content).buffer, { create: true, overwrite: true }); +} + +function createFolder(name: string): void { + simpleFileSystemProvider.mkdir(joinPath(workspaceResource, name)); +} + +createFolder(''); +createFolder('src'); +createFolder('test'); + +createFile('', '.gitignore', `out +node_modules +.vscode-test/ +*.vsix +`); + +createFile('', '.vscodeignore', `.vscode/** +.vscode-test/** +out/test/** +src/** +.gitignore +vsc-extension-quickstart.md +**/tsconfig.json +**/tslint.json +**/*.map +**/*.ts`); + +createFile('', 'CHANGELOG.md', `# Change Log +All notable changes to the "test-ts" extension will be documented in this file. + +Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. + +## [Unreleased] +- Initial release`); +createFile('', 'package.json', `{ + "name": "test-ts", + "displayName": "test-ts", + "description": "", + "version": "0.0.1", + "engines": { + "vscode": "^1.31.0" + }, + "categories": [ + "Other" + ], + "activationEvents": [ + "onCommand:extension.helloWorld" + ], + "main": "./out/extension.js", + "contributes": { + "commands": [ + { + "command": "extension.helloWorld", + "title": "Hello World" + } + ] + }, + "scripts": { + "vscode:prepublish": "npm run compile", + "compile": "tsc -p ./", + "watch": "tsc -watch -p ./", + "postinstall": "node ./node_modules/vscode/bin/install", + "test": "npm run compile && node ./node_modules/vscode/bin/test" + }, + "devDependencies": { + "typescript": "^3.3.1", + "vscode": "^1.1.28", + "tslint": "^5.12.1", + "@types/node": "^8.10.25", + "@types/mocha": "^2.2.42" + } +} +`); + +createFile('', 'tsconfig.json', `{ + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "outDir": "out", + "lib": [ + "es6" + ], + "sourceMap": true, + "rootDir": "src", + "strict": true /* enable all strict type-checking options */ + /* Additional Checks */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + }, + "exclude": [ + "node_modules", + ".vscode-test" + ] +} +`); + +createFile('', 'tslint.json', `{ + "rules": { + "no-string-throw": true, + "no-unused-expression": true, + "no-duplicate-variable": true, + "curly": true, + "class-name": true, + "semicolon": [ + true, + "always" + ], + "triple-equals": true + }, + "defaultSeverity": "warning" +} +`); + +createFile('src', 'extension.ts', `// The module 'vscode' contains the VS Code extensibility API +// Import the module and reference it with the alias vscode in your code below +import * as vscode from 'vscode'; + +// this method is called when your extension is activated +// your extension is activated the very first time the command is executed +export function activate(context: vscode.ExtensionContext) { + + // Use the console to output diagnostic information (console.log) and errors (console.error) + // This line of code will only be executed once when your extension is activated + console.log('Congratulations, your extension "test-ts" is now active!'); + + // The command has been defined in the package.json file + // Now provide the implementation of the command with registerCommand + // The commandId parameter must match the command field in package.json + let disposable = vscode.commands.registerCommand('extension.helloWorld', () => { + // The code you place here will be executed every time your command is executed + + // Display a message box to the user + vscode.window.showInformationMessage('Hello World!'); + }); + + context.subscriptions.push(disposable); +} + +// this method is called when your extension is deactivated +export function deactivate() {} +`); + +createFile('test', 'extension.test.ts', `// +// Note: This example test is leveraging the Mocha test framework. +// Please refer to their documentation on https://mochajs.org/ for help. +// + +// The module 'assert' provides assertion methods from node +import * as assert from 'assert'; + +// You can import and use all API from the 'vscode' module +// as well as import your extension to test it +// import * as vscode from 'vscode'; +// import * as myExtension from '../extension'; + +// Defines a Mocha test suite to group tests of similar kind together +suite("Extension Tests", function () { + + // Defines a Mocha unit test + test("Something 1", function() { + assert.equal(-1, [1, 2, 3].indexOf(5)); + assert.equal(-1, [1, 2, 3].indexOf(0)); + }); +});`); + +createFile('test', 'index.ts', `// +// PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING +// +// This file is providing the test runner to use when running extension tests. +// By default the test runner in use is Mocha based. +// +// You can provide your own test runner if you want to override it by exporting +// a function run(testRoot: string, clb: (error:Error) => void) that the extension +// host can call to run the tests. The test runner is expected to use console.log +// to report the results back to the caller. When the tests are finished, return +// a possible error to the callback or null if none. + +import * as testRunner from 'vscode/lib/testrunner'; + +// You can directly control Mocha options by configuring the test runner below +// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options +// for more info +testRunner.configure({ + ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) + useColors: true // colored output from test results +}); + +module.exports = testRunner;`); + +//#endregion + + +//#region Resource Identity + +export class SimpleResourceIdentityService implements IResourceIdentityService { + + declare readonly _serviceBrand: undefined; + + async resolveResourceIdentity(resource: URI): Promise { return hash(resource.toString()).toString(16); } +} + +//#endregion + + +//#region Remote + +export class SimpleRemoteAuthorityResolverService implements IRemoteAuthorityResolverService { + + declare readonly _serviceBrand: undefined; + + onDidChangeConnectionData: Event = Event.None; + resolveAuthority(authority: string): Promise { throw new Error('Method not implemented.'); } + getConnectionData(authority: string): IRemoteConnectionData | null { return null; } + _clearResolvedAuthority(authority: string): void { } + _setResolvedAuthority(resolvedAuthority: ResolvedAuthority, resolvedOptions?: ResolvedOptions): void { } + _setResolvedAuthorityError(authority: string, err: any): void { } + _setAuthorityConnectionToken(authority: string, connectionToken: string): void { } +} + +export class SimpleRemoteAgentService implements IRemoteAgentService { + + declare readonly _serviceBrand: undefined; + + socketFactory: ISocketFactory = new BrowserSocketFactory(null); + + getConnection(): IRemoteAgentConnection | null { return null; } + async getEnvironment(bail?: boolean): Promise { return null; } + async getDiagnosticInfo(options: IDiagnosticInfoOptions): Promise { return undefined; } + async disableTelemetry(): Promise { } + async logTelemetry(eventName: string, data?: ITelemetryData): Promise { } + async flushTelemetry(): Promise { } + async getRawEnvironment(): Promise { return null; } + async scanExtensions(skipExtensions?: ExtensionIdentifier[]): Promise { return []; } +} + +//#endregion + + +//#region Backup File + +class SimpleBackupFileService implements IBackupFileService { + + declare readonly _serviceBrand: undefined; + + async hasBackups(): Promise { return false; } + async discardResourceBackup(resource: URI): Promise { } + async discardAllWorkspaceBackups(): Promise { } + toBackupResource(resource: URI): URI { return resource; } + hasBackupSync(resource: URI, versionId?: number): boolean { return false; } + async getBackups(): Promise { return []; } + async resolve(resource: URI): Promise | undefined> { return undefined; } + async backup(resource: URI, content?: ITextSnapshot, versionId?: number, meta?: T): Promise { } + async discardBackup(resource: URI): Promise { } + async discardBackups(): Promise { } +} + +registerSingleton(IBackupFileService, SimpleBackupFileService); + +//#endregion + + +//#region Extensions + +class SimpleExtensionService extends NullExtensionService { } + +registerSingleton(IExtensionService, SimpleExtensionService); + +//#endregion + + +//#region Extensions Workbench (TODO@sandbox TODO@ben remove when 'semver-umd' can be loaded) + +class SimpleExtensionsWorkbenchService implements IExtensionsWorkbenchService { + + declare readonly _serviceBrand: undefined; + + onChange = Event.None; + + local = []; + installed = []; + outdated = []; + + queryGallery(...args: any[]): any { throw new Error('Method not implemented.'); } + install(...args: any[]): any { throw new Error('Method not implemented.'); } + queryLocal(server?: IExtensionManagementServer): Promise { throw new Error('Method not implemented.'); } + canInstall(extension: any): boolean { throw new Error('Method not implemented.'); } + uninstall(extension: any): Promise { throw new Error('Method not implemented.'); } + installVersion(extension: any, version: string): Promise { throw new Error('Method not implemented.'); } + reinstall(extension: any): Promise { throw new Error('Method not implemented.'); } + setEnablement(extensions: any | any[], enablementState: EnablementState): Promise { throw new Error('Method not implemented.'); } + open(extension: any, options?: { sideByside?: boolean | undefined; preserveFocus?: boolean | undefined; pinned?: boolean | undefined; }): Promise { throw new Error('Method not implemented.'); } + checkForUpdates(): Promise { throw new Error('Method not implemented.'); } + isExtensionIgnoredToSync(extension: any): boolean { throw new Error('Method not implemented.'); } + toggleExtensionIgnoredToSync(extension: any): Promise { throw new Error('Method not implemented.'); } +} + +registerSingleton(IExtensionsWorkbenchService, SimpleExtensionsWorkbenchService); + +//#endregion + +//#region Telemetry + +class SimpleTelemetryService implements ITelemetryService { + + declare readonly _serviceBrand: undefined; + + readonly sendErrorTelemetry = false; + readonly isOptedIn = false; + + async publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Promise { } + async publicLog2 = never, T extends GDPRClassification = never>(eventName: string, data?: StrictPropertyChecker, 'Type of classified event does not match event properties'>, anonymizeFilePaths?: boolean): Promise { } + async publicLogError(errorEventName: string, data?: ITelemetryData): Promise { } + async publicLogError2 = never, T extends GDPRClassification = never>(eventName: string, data?: StrictPropertyChecker, 'Type of classified event does not match event properties'>): Promise { } + setEnabled(value: boolean): void { } + setExperimentProperty(name: string, value: string): void { } + async getTelemetryInfo(): Promise { + return { + instanceId: 'someValue.instanceId', + sessionId: 'someValue.sessionId', + machineId: 'someValue.machineId' + }; + } +} + +registerSingleton(ITelemetryService, SimpleTelemetryService); + +//#endregion + + +//#region Keymap Service + +class SimpleKeyboardMapper implements IKeyboardMapper { + dumpDebugInfo(): string { return ''; } + resolveKeybinding(keybinding: ChordKeybinding): ResolvedKeybinding[] { return []; } + resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding { + let keybinding = new SimpleKeybinding( + keyboardEvent.ctrlKey, + keyboardEvent.shiftKey, + keyboardEvent.altKey, + keyboardEvent.metaKey, + keyboardEvent.keyCode + ).toChord(); + return new USLayoutResolvedKeybinding(keybinding, OS); + } + resolveUserBinding(firstPart: (SimpleKeybinding | ScanCodeBinding)[]): ResolvedKeybinding[] { return []; } +} + +class SimpleKeymapService implements IKeymapService { + + declare readonly _serviceBrand: undefined; + + onDidChangeKeyboardMapper = Event.None; + getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper { return new SimpleKeyboardMapper(); } + getCurrentKeyboardLayout(): (IWindowsKeyboardLayoutInfo & { isUserKeyboardLayout?: boolean | undefined; isUSStandard?: true | undefined; }) | (ILinuxKeyboardLayoutInfo & { isUserKeyboardLayout?: boolean | undefined; isUSStandard?: true | undefined; }) | (IMacKeyboardLayoutInfo & { isUserKeyboardLayout?: boolean | undefined; isUSStandard?: true | undefined; }) | null { return null; } + getAllKeyboardLayouts(): IKeyboardLayoutInfo[] { return []; } + getRawKeyboardMapping(): IWindowsKeyboardMapping | ILinuxKeyboardMapping | IMacKeyboardMapping | null { return null; } + validateCurrentKeyboardMapping(keyboardEvent: IKeyboardEvent): void { } +} + +registerSingleton(IKeymapService, SimpleKeymapService); + +//#endregion + + +//#region Path + +class SimplePathService implements IPathService { + + declare readonly _serviceBrand: undefined; + + readonly resolvedUserHome = URI.file('user-home'); + readonly path = Promise.resolve(OS === OperatingSystem.Windows ? win32 : posix); + + async fileURI(path: string): Promise { return URI.file(path); } + async userHome(options?: { preferLocal: boolean; }): Promise { return this.resolvedUserHome; } +} + +registerSingleton(IPathService, SimplePathService); + +//#endregion + + +//#region Dialog + +class SimpleDialogService implements IDialogService { + + declare readonly _serviceBrand: undefined; + + async confirm(confirmation: IConfirmation): Promise { return { confirmed: false }; } + async show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise { return { choice: 1 }; } + async about(): Promise { } +} + +registerSingleton(IDialogService, SimpleDialogService); + +//#endregion + + +//#region Webview + +class SimpleWebviewService implements IWebviewService { + declare readonly _serviceBrand: undefined; + + createWebviewElement(id: string, options: WebviewOptions, contentOptions: WebviewContentOptions, extension: WebviewExtensionDescription | undefined): WebviewElement { throw new Error('Method not implemented.'); } + createWebviewOverlay(id: string, options: WebviewOptions, contentOptions: WebviewContentOptions, extension: WebviewExtensionDescription | undefined): WebviewOverlay { throw new Error('Method not implemented.'); } + setIcons(id: string, value: WebviewIcons | undefined): void { } +} + +registerSingleton(IWebviewService, SimpleWebviewService); + +//#endregion + + +//#region Textfiles + +class SimpleTextFileService extends AbstractTextFileService { + declare readonly _serviceBrand: undefined; +} + +registerSingleton(ITextFileService, SimpleTextFileService); + +//#endregion + + +//#region extensions management + +class SimpleExtensionManagementServerService implements IExtensionManagementServerService { + + declare readonly _serviceBrand: undefined; + + readonly localExtensionManagementServer = null; + readonly remoteExtensionManagementServer = null; + readonly webExtensionManagementServer = null; + + getExtensionManagementServer(extension: IExtension): IExtensionManagementServer | null { return null; } +} + +registerSingleton(IExtensionManagementServerService, SimpleExtensionManagementServerService); + +//#endregion + + +//#region Textmate + +TokenizationRegistry.setColorMap([null!, new Color(new RGBA(212, 212, 212, 1)), new Color(new RGBA(30, 30, 30, 1))]); + +class SimpleTextMateService implements ITextMateService { + + declare readonly _serviceBrand: undefined; + + readonly onDidEncounterLanguage: Event = Event.None; + + async createGrammar(modeId: string): Promise { return null; } + startDebugMode(printFn: (str: string) => void, onStop: () => void): void { } +} + +registerSingleton(ITextMateService, SimpleTextMateService); + +//#endregion + + +//#region Accessibility + +class SimpleAccessibilityService implements IAccessibilityService { + + declare readonly _serviceBrand: undefined; + + onDidChangeScreenReaderOptimized = Event.None; + + isScreenReaderOptimized(): boolean { return false; } + async alwaysUnderlineAccessKeys(): Promise { return false; } + setAccessibilitySupport(accessibilitySupport: AccessibilitySupport): void { } + getAccessibilitySupport(): AccessibilitySupport { return AccessibilitySupport.Unknown; } +} + +registerSingleton(IAccessibilityService, SimpleAccessibilityService); + +//#endregion + + +//#region Tunnel + +class SimpleTunnelService implements ITunnelService { + + declare readonly _serviceBrand: undefined; + + tunnels: Promise = Promise.resolve([]); + + onTunnelOpened = Event.None; + onTunnelClosed = Event.None; + + openTunnel(addressProvider: IAddressProvider | undefined, remoteHost: string | undefined, remotePort: number, localPort?: number): Promise | undefined { return undefined; } + async closeTunnel(remoteHost: string, remotePort: number): Promise { } + setTunnelProvider(provider: ITunnelProvider | undefined): IDisposable { return Disposable.None; } +} + +registerSingleton(ITunnelService, SimpleTunnelService); + +//#endregion + + +//#region User Data Sync + +class SimpleUserDataSyncService implements IUserDataSyncService { + + declare readonly _serviceBrand: undefined; + + onDidChangeStatus = Event.None; + onDidChangeConflicts = Event.None; + onDidChangeLocal = Event.None; + onSyncErrors = Event.None; + onDidChangeLastSyncTime = Event.None; + onDidResetRemote = Event.None; + onDidResetLocal = Event.None; + + status: SyncStatus = SyncStatus.Idle; + conflicts: [SyncResource, IResourcePreview[]][] = []; + lastSyncTime = undefined; + + createSyncTask(): Promise { throw new Error('Method not implemented.'); } + createManualSyncTask(): Promise { throw new Error('Method not implemented.'); } + + async replace(uri: URI): Promise { } + async reset(): Promise { } + async resetRemote(): Promise { } + async resetLocal(): Promise { } + async hasLocalData(): Promise { return false; } + async hasPreviouslySynced(): Promise { return false; } + async resolveContent(resource: URI): Promise { return null; } + async accept(resource: SyncResource, conflictResource: URI, content: string | null | undefined, apply: boolean): Promise { } + async getLocalSyncResourceHandles(resource: SyncResource): Promise { return []; } + async getRemoteSyncResourceHandles(resource: SyncResource): Promise { return []; } + async getAssociatedResources(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise<{ resource: URI; comparableResource: URI; }[]> { return []; } + async getMachineId(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise { return undefined; } +} + +registerSingleton(IUserDataSyncService, SimpleUserDataSyncService); + +//#endregion + + +//#region User Data Sync Account + +class SimpleUserDataSyncAccountService implements IUserDataSyncAccountService { + + declare readonly _serviceBrand: undefined; + + onTokenFailed = Event.None; + onDidChangeAccount = Event.None; + + account: IUserDataSyncAccount | undefined = undefined; + + async updateAccount(account: IUserDataSyncAccount | undefined): Promise { } +} + +registerSingleton(IUserDataSyncAccountService, SimpleUserDataSyncAccountService); + +//#endregion + + +//#region User Data Auto Sync Account + +class SimpleUserDataAutoSyncAccountService implements IUserDataAutoSyncService { + + declare readonly _serviceBrand: undefined; + + onError = Event.None; + onDidChangeEnablement = Event.None; + + isEnabled(): boolean { return false; } + canToggleEnablement(): boolean { return false; } + async turnOn(): Promise { } + async turnOff(everywhere: boolean): Promise { } + async triggerSync(sources: string[], hasToLimitSync: boolean): Promise { } +} + +registerSingleton(IUserDataAutoSyncService, SimpleUserDataAutoSyncAccountService); + +//#endregion + + +//#region User Data Sync Store Management + +class SimpleIUserDataSyncStoreManagementService implements IUserDataSyncStoreManagementService { + + declare readonly _serviceBrand: undefined; + + userDataSyncStore: IUserDataSyncStore | undefined = undefined; + + async switch(type: UserDataSyncStoreType): Promise { } + + async getPreviousUserDataSyncStore(): Promise { return undefined; } +} + +registerSingleton(IUserDataSyncStoreManagementService, SimpleIUserDataSyncStoreManagementService); + +//#endregion + + +//#region Timer + +class SimpleTimerService extends AbstractTimerService { + protected _isInitialStartup(): boolean { return true; } + protected _didUseCachedData(): boolean { return false; } + protected async _getWindowCount(): Promise { return 1; } + protected async _extendStartupInfo(info: Writeable): Promise { } +} + +registerSingleton(ITimerService, SimpleTimerService); + +//#endregion + + +//#region Workspace Editing + +class SimpleWorkspaceEditingService implements IWorkspaceEditingService { + + declare readonly _serviceBrand: undefined; + + async addFolders(folders: IWorkspaceFolderCreationData[], donotNotifyError?: boolean): Promise { } + async removeFolders(folders: URI[], donotNotifyError?: boolean): Promise { } + async updateFolders(index: number, deleteCount?: number, foldersToAdd?: IWorkspaceFolderCreationData[], donotNotifyError?: boolean): Promise { } + async enterWorkspace(path: URI): Promise { } + async createAndEnterWorkspace(folders: IWorkspaceFolderCreationData[], path?: URI): Promise { } + async saveAndEnterWorkspace(path: URI): Promise { } + async copyWorkspaceSettings(toWorkspace: IWorkspaceIdentifier): Promise { } + async pickNewWorkspacePath(): Promise { return undefined!; } +} + +registerSingleton(IWorkspaceEditingService, SimpleWorkspaceEditingService); + +//#endregion + + +//#region Task + +class SimpleTaskService implements ITaskService { + + declare readonly _serviceBrand: undefined; + + onDidStateChange = Event.None; + supportsMultipleTaskExecutions = false; + + configureAction(): Action { throw new Error('Method not implemented.'); } + build(): Promise { throw new Error('Method not implemented.'); } + runTest(): Promise { throw new Error('Method not implemented.'); } + run(task: CustomTask | ContributedTask | InMemoryTask | undefined, options?: ProblemMatcherRunOptions): Promise { throw new Error('Method not implemented.'); } + inTerminal(): boolean { throw new Error('Method not implemented.'); } + isActive(): Promise { throw new Error('Method not implemented.'); } + getActiveTasks(): Promise { throw new Error('Method not implemented.'); } + getBusyTasks(): Promise { throw new Error('Method not implemented.'); } + restart(task: Task): void { throw new Error('Method not implemented.'); } + terminate(task: Task): Promise { throw new Error('Method not implemented.'); } + terminateAll(): Promise { throw new Error('Method not implemented.'); } + tasks(filter?: TaskFilter): Promise { throw new Error('Method not implemented.'); } + taskTypes(): string[] { throw new Error('Method not implemented.'); } + getWorkspaceTasks(runSource?: TaskRunSource): Promise> { throw new Error('Method not implemented.'); } + readRecentTasks(): Promise<(CustomTask | ContributedTask | InMemoryTask | ConfiguringTask)[]> { throw new Error('Method not implemented.'); } + getTask(workspaceFolder: string | IWorkspace | IWorkspaceFolder, alias: string | TaskIdentifier, compareId?: boolean): Promise { throw new Error('Method not implemented.'); } + tryResolveTask(configuringTask: ConfiguringTask): Promise { throw new Error('Method not implemented.'); } + getTasksForGroup(group: string): Promise { throw new Error('Method not implemented.'); } + getRecentlyUsedTasks(): LinkedMap { throw new Error('Method not implemented.'); } + migrateRecentTasks(tasks: Task[]): Promise { throw new Error('Method not implemented.'); } + createSorter(): TaskSorter { throw new Error('Method not implemented.'); } + getTaskDescription(task: CustomTask | ContributedTask | InMemoryTask | ConfiguringTask): string | undefined { throw new Error('Method not implemented.'); } + canCustomize(task: CustomTask | ContributedTask): boolean { throw new Error('Method not implemented.'); } + customize(task: CustomTask | ContributedTask | ConfiguringTask, properties?: {}, openConfig?: boolean): Promise { throw new Error('Method not implemented.'); } + openConfig(task: CustomTask | ConfiguringTask | undefined): Promise { throw new Error('Method not implemented.'); } + registerTaskProvider(taskProvider: ITaskProvider, type: string): IDisposable { throw new Error('Method not implemented.'); } + registerTaskSystem(scheme: string, taskSystemInfo: TaskSystemInfo): void { throw new Error('Method not implemented.'); } + registerSupportedExecutions(custom?: boolean, shell?: boolean, process?: boolean): void { throw new Error('Method not implemented.'); } + setJsonTasksSupported(areSuppored: Promise): void { throw new Error('Method not implemented.'); } + extensionCallbackTaskComplete(task: Task, result: number | undefined): Promise { throw new Error('Method not implemented.'); } +} + +registerSingleton(ITaskService, SimpleTaskService); + +//#endregion + + +//#region Extension Management + +class SimpleExtensionManagementService implements IExtensionManagementService { + + declare readonly _serviceBrand: undefined; + + onInstallExtension = Event.None; + onDidInstallExtension = Event.None; + onUninstallExtension = Event.None; + onDidUninstallExtension = Event.None; + + async zip(extension: ILocalExtension): Promise { throw new Error('Method not implemented.'); } + async unzip(zipLocation: URI): Promise { throw new Error('Method not implemented.'); } + async getManifest(vsix: URI): Promise { throw new Error('Method not implemented.'); } + async install(vsix: URI, isMachineScoped?: boolean): Promise { throw new Error('Method not implemented.'); } + async installFromGallery(extension: IGalleryExtension, isMachineScoped?: boolean): Promise { throw new Error('Method not implemented.'); } + async uninstall(extension: ILocalExtension, force?: boolean): Promise { } + async reinstallFromGallery(extension: ILocalExtension): Promise { } + async getInstalled(type?: ExtensionType): Promise { return []; } + async getExtensionsReport(): Promise { return []; } + async updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise { throw new Error('Method not implemented.'); } +} + +registerSingleton(IExtensionManagementService, SimpleExtensionManagementService); + +//#endregion + + +//#region Extension Tips + +class SimpleExtensionTipsService implements IExtensionTipsService { + + declare readonly _serviceBrand: undefined; + + onRecommendationChange = Event.None; + + getAllRecommendationsWithReason(): { [id: string]: { reasonId: ExtensionRecommendationReason; reasonText: string; }; } { return Object.create(null); } + getFileBasedRecommendations(): IExtensionRecommendation[] { return []; } + async getOtherRecommendations(): Promise { return []; } + async getWorkspaceRecommendations(): Promise { return []; } + getKeymapRecommendations(): IExtensionRecommendation[] { return []; } + toggleIgnoredRecommendation(extensionId: string, shouldIgnore: boolean): void { } + getAllIgnoredRecommendations(): { global: string[]; workspace: string[]; } { return Object.create(null); } + async getConfigBasedTips(folder: URI): Promise { return []; } + async getImportantExecutableBasedTips(): Promise { return []; } + async getOtherExecutableBasedTips(): Promise { return []; } + async getAllWorkspacesTips(): Promise { return []; } +} + +registerSingleton(IExtensionTipsService, SimpleExtensionTipsService); + +//#endregion + + +//#region Workspace Tags + +class SimpleWorkspaceTagsService implements IWorkspaceTagsService { + + declare readonly _serviceBrand: undefined; + + async getTags(): Promise { return Object.create(null); } + getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): string | undefined { return undefined; } + async getHashedRemotesFromUri(workspaceUri: URI, stripEndingDotGit?: boolean): Promise { return []; } +} + +registerSingleton(IWorkspaceTagsService, SimpleWorkspaceTagsService); + +//#endregion + + +//#region Output Channel + +class SimpleOutputChannelModelService extends AsbtractOutputChannelModelService { + declare readonly _serviceBrand: undefined; +} + +registerSingleton(IOutputChannelModelService, SimpleOutputChannelModelService); + +//#endregion diff --git a/src/vs/workbench/workbench.desktop.sandbox.main.ts b/src/vs/workbench/workbench.desktop.sandbox.main.ts new file mode 100644 index 00000000000..1d2f2a4150d --- /dev/null +++ b/src/vs/workbench/workbench.desktop.sandbox.main.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +// ####################################################################### +// ### ### +// ### !!! PLEASE ADD COMMON IMPORTS INTO WORKBENCH.COMMON.MAIN.TS !!! ### +// ### ### +// ####################################################################### + + +//#region --- workbench common & sandbox + +import 'vs/workbench/workbench.sandbox.main'; + +//#endregion + + +//#region --- workbench actions + + +//#endregion + + +//#region --- workbench (desktop main) + +import 'vs/workbench/electron-sandbox/desktop.main'; + +//#endregion + + +//#region --- workbench services + + +//#endregion + + +//#region --- workbench contributions + + +//#endregion diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index b956a2b5918..e6452bb627c 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -130,4 +130,7 @@ import 'vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.con // Issues import 'vs/workbench/contrib/issue/browser/issue.web.contribution'; +// Extensions Management (// TODO@sandbox TODO@ben move back into common/extensions.contribution.ts when 'semver-umd' can be loaded) +import 'vs/workbench/contrib/extensions/browser/extensions.web.contribution'; + //#endregion From b0f04b60e52ecbfa5dfe846eb3eb9cfb322c6479 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Wed, 26 Aug 2020 02:48:41 -0400 Subject: [PATCH 529/736] Fixing build --- src/vs/platform/contextkey/common/contextkey.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts index 6f0fb98af61..8cdc6954c0a 100644 --- a/src/vs/platform/contextkey/common/contextkey.ts +++ b/src/vs/platform/contextkey/common/contextkey.ts @@ -409,7 +409,7 @@ export class ContextKeyEqualsExpr implements IContextKeyExpression { export class ContextKeyInExpr implements IContextKeyExpression { - public static create(key: string, valueKey: string): ContextKeyExpression { + public static create(key: string, valueKey: string): ContextKeyInExpr { return new ContextKeyInExpr(key, valueKey); } @@ -478,7 +478,7 @@ export class ContextKeyInExpr implements IContextKeyExpression { export class ContextKeyNotInExpr implements IContextKeyExpression { - public static create(actual: ContextKeyInExpr): ContextKeyExpression { + public static create(actual: ContextKeyInExpr): ContextKeyNotInExpr { return new ContextKeyNotInExpr(actual); } From e7da3681fe842f8a66373981c868f55bae0babd0 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Wed, 26 Aug 2020 03:01:24 -0400 Subject: [PATCH 530/736] Updates distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1858ff663b8..057ff7ea4fe 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.49.0", - "distro": "30ea358b7ad9b2e701d90077c2ff970e87a7a06b", + "distro": "7a9bb68effb9d25a544159a0c85e3c20c4ba852e", "author": { "name": "Microsoft Corporation" }, From 7df29b1236ecf8a21af9fe19a52923f6d69e0577 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 26 Aug 2020 09:07:44 +0200 Subject: [PATCH 531/736] sandbox - fix for windows --- src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts index c8b70e5f28d..8fff37e1f8d 100644 --- a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts +++ b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts @@ -70,7 +70,7 @@ import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/com //#region Workspace -export const workspaceResource = URI.file(isWindows ? 'C:\\simpleWorkspace' : '/simpleWorkspace'); +export const workspaceResource = URI.file(isWindows ? '\\simpleWorkspace' : '/simpleWorkspace'); export class SimpleWorkspaceService implements IWorkspaceContextService { From 3503c0124a6f71316b4bd69f87575489e6f10d23 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 26 Aug 2020 10:04:26 +0200 Subject: [PATCH 532/736] fuzzy scorer - make sure to give prefix scores the same fuzzy scoring treatment This fixes issues around multi-queries, where each segment has a prefix match. --- src/vs/base/common/fuzzyScorer.ts | 72 ++++++--------------- src/vs/base/test/common/fuzzyScorer.test.ts | 15 +++++ 2 files changed, 36 insertions(+), 51 deletions(-) diff --git a/src/vs/base/common/fuzzyScorer.ts b/src/vs/base/common/fuzzyScorer.ts index c0c235c6874..ad53ee315be 100644 --- a/src/vs/base/common/fuzzyScorer.ts +++ b/src/vs/base/common/fuzzyScorer.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { compareAnything } from 'vs/base/common/comparers'; -import { matchesPrefix, IMatch, isUpper, fuzzyScore, createMatches as createFuzzyMatches, matchesStrictPrefix } from 'vs/base/common/filters'; +import { matchesPrefix, IMatch, isUpper, fuzzyScore, createMatches as createFuzzyMatches } from 'vs/base/common/filters'; import { sep } from 'vs/base/common/path'; import { isWindows, isLinux } from 'vs/base/common/platform'; import { stripWildcards, equalsIgnoreCase } from 'vs/base/common/strings'; @@ -369,9 +369,8 @@ export interface IItemAccessor { } const PATH_IDENTITY_SCORE = 1 << 18; -const LABEL_PREFIX_SCORE_MATCHCASE = 1 << 17; -const LABEL_PREFIX_SCORE_IGNORECASE = 1 << 16; -const LABEL_SCORE_THRESHOLD = 1 << 15; +const LABEL_PREFIX_SCORE_THRESHOLD = 1 << 17; +const LABEL_SCORE_THRESHOLD = 1 << 16; export function scoreItemFuzzy(item: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor, cache: FuzzyScorerCache): IItemScore { if (!item || !query.normalized) { @@ -460,20 +459,21 @@ function doScoreItemFuzzyMultiple(label: string, description: string | undefined function doScoreItemFuzzySingle(label: string, description: string | undefined, path: string | undefined, query: IPreparedQueryPiece, preferLabelMatches: boolean, fuzzy: boolean): IItemScore { - // Prefer label matches if told so - if (preferLabelMatches) { - - // Treat prefix matches on the label highest - const prefixLabelMatchIgnoreCase = matchesPrefix(query.normalized, label); - if (prefixLabelMatchIgnoreCase) { - const prefixLabelMatchStrictCase = matchesStrictPrefix(query.normalized, label); - return { score: prefixLabelMatchStrictCase ? LABEL_PREFIX_SCORE_MATCHCASE : LABEL_PREFIX_SCORE_IGNORECASE, labelMatch: prefixLabelMatchStrictCase || prefixLabelMatchIgnoreCase }; - } - - // Second, score fuzzy + // Prefer label matches if told so or we have no description + if (preferLabelMatches || !description) { const [labelScore, labelPositions] = scoreFuzzy(label, query.normalized, query.normalizedLowercase, fuzzy); if (labelScore) { - return { score: labelScore + LABEL_SCORE_THRESHOLD, labelMatch: createMatches(labelPositions) }; + + // If we have a prefix match on the label, we give a much + // higher baseScore to elevate these matches over others + let baseScore: number; + if (matchesPrefix(query.normalized, label)) { + baseScore = LABEL_PREFIX_SCORE_THRESHOLD; + } else { + baseScore = LABEL_SCORE_THRESHOLD; + } + + return { score: baseScore + labelScore, labelMatch: createMatches(labelPositions) }; } } @@ -600,37 +600,7 @@ export function compareItemsByFuzzyScore(itemA: T, itemB: T, query: IPrepared } } - // 2.) prefer label prefix matches (match case) - if (scoreA === LABEL_PREFIX_SCORE_MATCHCASE || scoreB === LABEL_PREFIX_SCORE_MATCHCASE) { - if (scoreA !== scoreB) { - return scoreA === LABEL_PREFIX_SCORE_MATCHCASE ? -1 : 1; - } - - const labelA = accessor.getItemLabel(itemA) || ''; - const labelB = accessor.getItemLabel(itemB) || ''; - - // prefer shorter names when both match on label prefix - if (labelA.length !== labelB.length) { - return labelA.length - labelB.length; - } - } - - // 3.) prefer label prefix matches (ignore case) - if (scoreA === LABEL_PREFIX_SCORE_IGNORECASE || scoreB === LABEL_PREFIX_SCORE_IGNORECASE) { - if (scoreA !== scoreB) { - return scoreA === LABEL_PREFIX_SCORE_IGNORECASE ? -1 : 1; - } - - const labelA = accessor.getItemLabel(itemA) || ''; - const labelB = accessor.getItemLabel(itemB) || ''; - - // prefer shorter names when both match on label prefix - if (labelA.length !== labelB.length) { - return labelA.length - labelB.length; - } - } - - // 4.) matches on label are considered higher compared to label+description matches + // 2.) matches on label are considered higher compared to label+description matches if (scoreA > LABEL_SCORE_THRESHOLD || scoreB > LABEL_SCORE_THRESHOLD) { if (scoreA !== scoreB) { return scoreA > scoreB ? -1 : 1; @@ -650,12 +620,12 @@ export function compareItemsByFuzzyScore(itemA: T, itemB: T, query: IPrepared } } - // 5.) compare by score in label+description + // 3.) compare by score in label+description if (scoreA !== scoreB) { return scoreA > scoreB ? -1 : 1; } - // 6.) scores are identical: prefer matches in label over non-label matches + // 4.) scores are identical: prefer matches in label over non-label matches const itemAHasLabelMatches = Array.isArray(itemScoreA.labelMatch) && itemScoreA.labelMatch.length > 0; const itemBHasLabelMatches = Array.isArray(itemScoreB.labelMatch) && itemScoreB.labelMatch.length > 0; if (itemAHasLabelMatches && !itemBHasLabelMatches) { @@ -664,14 +634,14 @@ export function compareItemsByFuzzyScore(itemA: T, itemB: T, query: IPrepared return 1; } - // 7.) scores are identical: prefer more compact matches (label and description) + // 5.) scores are identical: prefer more compact matches (label and description) const itemAMatchDistance = computeLabelAndDescriptionMatchDistance(itemA, itemScoreA, accessor); const itemBMatchDistance = computeLabelAndDescriptionMatchDistance(itemB, itemScoreB, accessor); if (itemAMatchDistance && itemBMatchDistance && itemAMatchDistance !== itemBMatchDistance) { return itemBMatchDistance > itemAMatchDistance ? -1 : 1; } - // 8.) scores are identical: start to use the fallback compare + // 6.) scores are identical: start to use the fallback compare return fallbackCompare(itemA, itemB, query, accessor); } diff --git a/src/vs/base/test/common/fuzzyScorer.test.ts b/src/vs/base/test/common/fuzzyScorer.test.ts index 278f2bf5edb..1a7eca591ff 100644 --- a/src/vs/base/test/common/fuzzyScorer.test.ts +++ b/src/vs/base/test/common/fuzzyScorer.test.ts @@ -1025,6 +1025,21 @@ suite('Fuzzy Scorer', () => { assert.equal(res[1], resourceB); }); + test('compareFilesByScore - boost better prefix match if multiple queries are used', function () { + const resourceA = URI.file('src/vs/workbench/services/host/browser/browserHostService.ts'); + const resourceB = URI.file('src/vs/workbench/browser/workbench.ts'); + + for (const query of ['workbench.ts browser', 'browser workbench.ts']) { + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceB); + assert.equal(res[1], resourceA); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceB); + assert.equal(res[1], resourceA); + } + }); + test('prepareQuery', () => { assert.equal(scorer.prepareQuery(' f*a ').normalized, 'fa'); assert.equal(scorer.prepareQuery('model Tester.ts').original, 'model Tester.ts'); From 52b5ca614a0681b9697d8cd5ca5329b26a73eeb4 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Aug 2020 10:34:37 +0200 Subject: [PATCH 533/736] use language name in recommendation --- .../extensions/browser/fileBasedRecommendations.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts index 15653906611..1e0208da2aa 100644 --- a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts @@ -161,7 +161,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { } private async promptRecommendations(uri: URI, fileExtension: string): Promise { - const recommendationsToPrompt: string[] = []; + const recommendationsToPrompt: { extensionId: string, languageName: string }[] = []; forEach(this.fileBasedRecommendationsByPattern, ({ key: pattern, value: extensionIds }) => { if (match(pattern, uri.toString())) { for (const extensionId of extensionIds) { @@ -169,7 +169,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { // Only prompt if the pattern matches the extensionImportantTips pattern // Otherwise, assume pattern is from extensionTips, which means it should be a file based "passive" recommendation if (this.importantExtensionTips[extensionId]?.pattern === pattern) { - recommendationsToPrompt.push(extensionId); + recommendationsToPrompt.push({ extensionId, languageName: this.importantExtensionTips[extensionId].name }); } // Update file based recommendations const filedBasedRecommendation = this.fileBasedRecommendations[extensionId] || { recommendedTime: Date.now(), sources: [] }; @@ -189,7 +189,8 @@ export class FileBasedRecommendations extends ExtensionRecommendations { } const installed = await this.extensionsWorkbenchService.queryLocal(); - if (await this.promptRecommendedExtensionForFileType(fileExtension.substring(1), recommendationsToPrompt, installed)) { + if (recommendationsToPrompt.length && + await this.promptRecommendedExtensionForFileType(fileExtension.substring(1), recommendationsToPrompt[0].languageName, recommendationsToPrompt.map(r => r.extensionId), installed)) { return; } @@ -209,7 +210,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { this.promptRecommendedExtensionForFileExtension(fileExtension, installed); } - private async promptRecommendedExtensionForFileType(ext: string, recommendations: string[], installed: IExtension[]): Promise { + private async promptRecommendedExtensionForFileType(ext: string, languageName: string, recommendations: string[], installed: IExtension[]): Promise { recommendations = this.filterIgnoredOrNotAllowed(recommendations); if (recommendations.length === 0) { @@ -227,7 +228,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { return false; } - this.promptImportantExtensionsInstallNotification([extensionId], localize('reallyRecommended', "Do you want to install support for this file?"), `ext:${ext}`); + this.promptImportantExtensionsInstallNotification([extensionId], localize('reallyRecommended', "Do you want to install support for {0}?", languageName), `ext:${ext}`); return true; } From 466cc0f807a515186cc93624d43b4cca6a94fcba Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Aug 2020 11:49:32 +0200 Subject: [PATCH 534/736] align workspace recommendations --- .../browser/configBasedRecommendations.ts | 52 +------- .../extensionRecommendationsService.ts | 115 ++++++++++++++++-- .../browser/workspaceRecommendations.ts | 86 ++----------- .../extensionRecommendationsService.test.ts | 20 +-- 4 files changed, 128 insertions(+), 145 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts index 61ab9d55b35..10f9e97cad2 100644 --- a/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IExtensionTipsService, IExtensionManagementService, ILocalExtension, IConfigBasedExtensionTip } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionTipsService, IConfigBasedExtensionTip } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { localize } from 'vs/nls'; @@ -14,13 +14,16 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IStorageService } from 'vs/platform/storage/common/storage'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { IWorkspaceContextService, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; -import { distinct } from 'vs/base/common/arrays'; +import { Emitter } from 'vs/base/common/event'; export class ConfigBasedRecommendations extends ExtensionRecommendations { private importantTips: IConfigBasedExtensionTip[] = []; private otherTips: IConfigBasedExtensionTip[] = []; + private _onDidChangeRecommendations = this._register(new Emitter()); + readonly onDidChangeRecommendations = this._onDidChangeRecommendations.event; + private _otherRecommendations: ExtensionRecommendation[] = []; get otherRecommendations(): ReadonlyArray { return this._otherRecommendations; } @@ -32,7 +35,6 @@ export class ConfigBasedRecommendations extends ExtensionRecommendations { constructor( isExtensionAllowedToBeRecommended: (extensionId: string) => boolean, @IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService, - @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @IInstantiationService instantiationService: IInstantiationService, @IConfigurationService configurationService: IConfigurationService, @@ -47,7 +49,6 @@ export class ConfigBasedRecommendations extends ExtensionRecommendations { protected async doActivate(): Promise { await this.fetch(); this._register(this.workspaceContextService.onDidChangeWorkspaceFolders(e => this.onWorkspaceFoldersChanged(e))); - this.promptWorkspaceRecommendations(); } private async fetch(): Promise { @@ -70,54 +71,13 @@ export class ConfigBasedRecommendations extends ExtensionRecommendations { this._importantRecommendations = this.importantTips.map(tip => this.toExtensionRecommendation(tip)); } - private async promptWorkspaceRecommendations(): Promise { - if (this.hasToIgnoreRecommendationNotifications()) { - return; - } - - if (this.importantTips.length === 0) { - return; - } - - const local = await this.extensionManagementService.getInstalled(); - const { uninstalled } = this.groupByInstalled(distinct(this.importantTips.map(({ extensionId }) => extensionId)), local); - if (uninstalled.length === 0) { - return; - } - - const importantExtensions = this.filterIgnoredOrNotAllowed(uninstalled); - if (importantExtensions.length === 0) { - return; - } - - for (const extension of importantExtensions) { - const tip = this.importantTips.filter(tip => tip.extensionId === extension)[0]; - const message = tip.isExtensionPack ? localize('extensionPackRecommended', "The '{0}' extension pack is recommended for this workspace.", tip.extensionName) - : localize('extensionRecommended', "The '{0}' extension is recommended for this workspace.", tip.extensionName); - this.promptImportantExtensionsInstallNotification([extension], message, extension, localize('more information', "More Information")); - } - } - - private groupByInstalled(recommendationsToSuggest: string[], local: ILocalExtension[]): { installed: string[], uninstalled: string[] } { - const installed: string[] = [], uninstalled: string[] = []; - const installedExtensionsIds = local.reduce((result, i) => { result.add(i.identifier.id.toLowerCase()); return result; }, new Set()); - recommendationsToSuggest.forEach(id => { - if (installedExtensionsIds.has(id.toLowerCase())) { - installed.push(id); - } else { - uninstalled.push(id); - } - }); - return { installed, uninstalled }; - } - private async onWorkspaceFoldersChanged(event: IWorkspaceFoldersChangeEvent): Promise { if (event.added.length) { const oldImportantRecommended = this.importantTips; await this.fetch(); // Suggest only if at least one of the newly added recommendations was not suggested before if (this.importantTips.some(current => oldImportantRecommended.every(old => current.extensionId !== old.extensionId))) { - return this.promptWorkspaceRecommendations(); + this._onDidChangeRecommendations.fire(); } } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts index d844e46c511..907c686d16d 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts @@ -5,10 +5,10 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IExtensionManagementService, IExtensionGalleryService, InstallOperation, DidInstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IExtensionRecommendationsService, ExtensionRecommendationReason, RecommendationChangeNotification, IExtensionRecommendation, ExtensionRecommendationSource } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionRecommendationsService, ExtensionRecommendationReason, RecommendationChangeNotification, IExtensionRecommendation, ExtensionRecommendationSource, EnablementState, IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ShowRecommendationsOnlyOnDemandKey } from 'vs/workbench/contrib/extensions/common/extensions'; +import { ConfigurationKey, IExtensionsConfiguration, ShowRecommendationsOnlyOnDemandKey } from 'vs/workbench/contrib/extensions/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { distinct, shuffle } from 'vs/base/common/arrays'; @@ -25,13 +25,23 @@ import { KeymapRecommendations } from 'vs/workbench/contrib/extensions/browser/k import { ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { ConfigBasedRecommendations } from 'vs/workbench/contrib/extensions/browser/configBasedRecommendations'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import Severity from 'vs/base/common/severity'; +import { localize } from 'vs/nls'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { InstallRecommendedExtensionsAction, SearchExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; type IgnoreRecommendationClassification = { recommendationReason: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; extensionId: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; }; +type ExtensionWorkspaceRecommendationsNotificationClassification = { + userReaction: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; +}; + const ignoredRecommendationsStorageKey = 'extensionsAssistant/ignored_recommendations'; +const ignoreWorkspaceRecommendationsStorageKey = 'extensionsAssistant/workspaceRecommendationsIgnore'; export class ExtensionRecommendationsService extends Disposable implements IExtensionRecommendationsService { @@ -49,15 +59,15 @@ export class ExtensionRecommendationsService extends Disposable implements IExte // Ignored Recommendations private globallyIgnoredRecommendations: string[] = []; - public loadWorkspaceConfigPromise: Promise; + public readonly activationPromise: Promise; private sessionSeed: number; private readonly _onRecommendationChange = this._register(new Emitter()); onRecommendationChange: Event = this._onRecommendationChange.event; constructor( - @IInstantiationService instantiationService: IInstantiationService, - @ILifecycleService lifecycleService: ILifecycleService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ILifecycleService private readonly lifecycleService: ILifecycleService, @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, @IStorageService private readonly storageService: IStorageService, @@ -65,6 +75,8 @@ export class ExtensionRecommendationsService extends Disposable implements IExte @ITelemetryService private readonly telemetryService: ITelemetryService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, + @IWorkbenchExtensionEnablementService protected readonly extensionEnablementService: IWorkbenchExtensionEnablementService, + @INotificationService protected readonly notificationService: INotificationService, ) { super(); @@ -81,7 +93,7 @@ export class ExtensionRecommendationsService extends Disposable implements IExte if (!this.isEnabled()) { this.sessionSeed = 0; - this.loadWorkspaceConfigPromise = Promise.resolve(); + this.activationPromise = Promise.resolve(); return; } @@ -89,17 +101,33 @@ export class ExtensionRecommendationsService extends Disposable implements IExte this.globallyIgnoredRecommendations = this.getCachedIgnoredRecommendations(); // Activation - this.loadWorkspaceConfigPromise = this.workspaceRecommendations.activate().then(() => this.fileBasedRecommendations.activate()); - this.experimentalRecommendations.activate(); - this.keymapRecommendations.activate(); - if (!this.configurationService.getValue(ShowRecommendationsOnlyOnDemandKey)) { - lifecycleService.when(LifecyclePhase.Eventually).then(() => this.activateProactiveRecommendations()); - } + this.activationPromise = this.activate(); this._register(this.extensionManagementService.onDidInstallExtension(e => this.onDidInstallExtension(e))); this._register(this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e))); } + private async activate(): Promise { + await this.lifecycleService.when(LifecyclePhase.Restored); + + // activate all recommendations + await Promise.all([ + this.workspaceRecommendations.activate(), + this.fileBasedRecommendations.activate(), + this.experimentalRecommendations.activate(), + this.keymapRecommendations.activate(), + this.lifecycleService.when(LifecyclePhase.Eventually) + .then(() => { + if (!this.configurationService.getValue(ShowRecommendationsOnlyOnDemandKey)) { + this.activateProactiveRecommendations(); + } + }) + ]); + + this.promptWorkspaceRecommendations(); + this._register(Event.any(this.workspaceRecommendations.onDidChangeRecommendations, this.configBasedRecommendations.onDidChangeRecommendations)(() => this.promptWorkspaceRecommendations())); + } + private isEnabled(): boolean { return this.galleryService.isEnabled() && !this.environmentService.extensionDevelopmentLocationURI; } @@ -261,6 +289,69 @@ export class ExtensionRecommendationsService extends Disposable implements IExte return allIgnoredRecommendations.indexOf(id.toLowerCase()) === -1; } + private async promptWorkspaceRecommendations(): Promise { + const allowedRecommendations = [...this.workspaceRecommendations.recommendations, ...this.configBasedRecommendations.importantRecommendations] + .map(({ extensionId }) => extensionId) + .filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId)); + + const config = this.configurationService.getValue(ConfigurationKey); + if (allowedRecommendations.length === 0 + || config.ignoreRecommendations || config.showRecommendationsOnlyOnDemand + || this.storageService.getBoolean(ignoreWorkspaceRecommendationsStorageKey, StorageScope.WORKSPACE, false)) { + return; + } + + let installed = await this.extensionManagementService.getInstalled(); + installed = installed.filter(l => this.extensionEnablementService.getEnablementState(l) !== EnablementState.DisabledByExtensionKind); // Filter extensions disabled by kind + const recommendations = allowedRecommendations.filter(extensionId => installed.every(local => !areSameExtensions({ id: extensionId }, local.identifier))); + + if (!recommendations.length) { + return; + } + + const searchValue = '@recommended '; + this.notificationService.prompt( + Severity.Info, + localize('workspaceRecommended', "Do you want to install support for this workspace?"), + [{ + label: localize('install', "Install"), + run: async () => { + this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'install' }); + const action = this.instantiationService.createInstance(InstallRecommendedExtensionsAction, InstallRecommendedExtensionsAction.ID, InstallRecommendedExtensionsAction.LABEL, recommendations, searchValue, 'install-all-workspace-recommendations'); + try { + await action.run(); + } finally { + action.dispose(); + } + } + }, { + label: localize('showRecommendations', "Show Recommendations"), + run: async () => { + this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'show' }); + const action = this.instantiationService.createInstance(SearchExtensionsAction, searchValue); + try { + await action.run(); + } finally { + action.dispose(); + } + } + }, { + label: localize('neverShowAgain', "Don't Show Again"), + isSecondary: true, + run: () => { + this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'neverShowAgain' }); + this.storageService.store(ignoreWorkspaceRecommendationsStorageKey, true, StorageScope.WORKSPACE); + } + }], + { + sticky: true, + onCancel: () => { + this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'cancelled' }); + } + } + ); + } + private onDidStorageChange(e: IWorkspaceStorageChangeEvent): void { if (e.key === ignoredRecommendationsStorageKey && e.scope === StorageScope.GLOBAL && this.ignoredRecommendationsValue !== this.getStoredIgnoredRecommendationsValue() /* This checks if current window changed the value or not */) { diff --git a/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts index 889ba988957..80eab437ff4 100644 --- a/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts @@ -3,38 +3,33 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EXTENSION_IDENTIFIER_PATTERN, IExtensionGalleryService, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { EXTENSION_IDENTIFIER_PATTERN, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkspaceContextService, IWorkspaceFolder, IWorkspace, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; import { IFileService } from 'vs/platform/files/common/files'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { distinct, flatten, coalesce } from 'vs/base/common/arrays'; import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -import { IExtensionsConfigContent, ExtensionRecommendationSource, ExtensionRecommendationReason, IWorkbenchExtensionEnablementService, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IExtensionsConfigContent, ExtensionRecommendationSource, ExtensionRecommendationReason } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { parse } from 'vs/base/common/json'; import { EXTENSIONS_CONFIG } from 'vs/workbench/contrib/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; import { localize } from 'vs/nls'; -import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { ShowRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; -import { StorageScope, IStorageService } from 'vs/platform/storage/common/storage'; +import { IStorageService } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; - -type ExtensionWorkspaceRecommendationsNotificationClassification = { - userReaction: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; -}; - -const choiceNever = localize('neverShowAgain', "Don't Show Again"); -const ignoreWorkspaceRecommendationsStorageKey = 'extensionsAssistant/workspaceRecommendationsIgnore'; +import { Emitter } from 'vs/base/common/event'; export class WorkspaceRecommendations extends ExtensionRecommendations { private _recommendations: ExtensionRecommendation[] = []; get recommendations(): ReadonlyArray { return this._recommendations; } + private _onDidChangeRecommendations = this._register(new Emitter()); + readonly onDidChangeRecommendations = this._onDidChangeRecommendations.event; + private _ignoredRecommendations: string[] = []; get ignoredRecommendations(): ReadonlyArray { return this._ignoredRecommendations; } @@ -44,8 +39,6 @@ export class WorkspaceRecommendations extends ExtensionRecommendations { @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, @ILogService private readonly logService: ILogService, @IFileService private readonly fileService: IFileService, - @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, - @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, @IInstantiationService instantiationService: IInstantiationService, @IConfigurationService configurationService: IConfigurationService, @INotificationService notificationService: INotificationService, @@ -59,7 +52,6 @@ export class WorkspaceRecommendations extends ExtensionRecommendations { protected async doActivate(): Promise { await this.fetch(); this._register(this.contextService.onDidChangeWorkspaceFolders(e => this.onWorkspaceFoldersChanged(e))); - this.promptWorkspaceRecommendations(); } /** @@ -97,63 +89,6 @@ export class WorkspaceRecommendations extends ExtensionRecommendations { } } - private async promptWorkspaceRecommendations(): Promise { - const allowedRecommendations = this.recommendations.filter(rec => this.isExtensionAllowedToBeRecommended(rec.extensionId)); - - if (allowedRecommendations.length === 0 || this.hasToIgnoreWorkspaceRecommendationNotifications()) { - return; - } - - let installed = await this.extensionManagementService.getInstalled(); - installed = installed.filter(l => this.extensionEnablementService.getEnablementState(l) !== EnablementState.DisabledByExtensionKind); // Filter extensions disabled by kind - const recommendations = allowedRecommendations.filter(({ extensionId }) => installed.every(local => !areSameExtensions({ id: extensionId }, local.identifier))); - - if (!recommendations.length) { - return; - } - - return new Promise(c => { - this.notificationService.prompt( - Severity.Info, - localize('workspaceRecommended', "This workspace has extension recommendations."), - [{ - label: localize('installAll', "Install All"), - run: () => { - this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'install' }); - const installAllAction = this.instantiationService.createInstance(InstallWorkspaceRecommendedExtensionsAction, recommendations.map(({ extensionId }) => extensionId)); - installAllAction.run(); - installAllAction.dispose(); - c(undefined); - } - }, { - label: localize('showRecommendations', "Show Recommendations"), - run: () => { - this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'show' }); - const showAction = this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, localize('showRecommendations', "Show Recommendations")); - showAction.run(); - showAction.dispose(); - c(undefined); - } - }, { - label: choiceNever, - isSecondary: true, - run: () => { - this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'neverShowAgain' }); - this.storageService.store(ignoreWorkspaceRecommendationsStorageKey, true, StorageScope.WORKSPACE); - c(undefined); - } - }], - { - sticky: true, - onCancel: () => { - this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'cancelled' }); - c(undefined); - } - } - ); - }); - } - private async fetchExtensionsConfigBySource(): Promise<{ contents: IExtensionsConfigContent, source: ExtensionRecommendationSource }[]> { const workspace = this.contextService.getWorkspace(); const result = await Promise.all([ @@ -235,7 +170,7 @@ export class WorkspaceRecommendations extends ExtensionRecommendations { await this.fetch(); // Suggest only if at least one of the newly added recommendations was not suggested before if (this._recommendations.some(current => oldWorkspaceRecommended.every(old => current.extensionId !== old.extensionId))) { - this.promptWorkspaceRecommendations(); + this._onDidChangeRecommendations.fire(); } } } @@ -250,8 +185,5 @@ export class WorkspaceRecommendations extends ExtensionRecommendations { return null; } - private hasToIgnoreWorkspaceRecommendationNotifications(): boolean { - return this.hasToIgnoreRecommendationNotifications() || this.storageService.getBoolean(ignoreWorkspaceRecommendationsStorageKey, StorageScope.WORKSPACE, false); - } } diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts index b20db7d9a94..e49f190ae1e 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts @@ -302,7 +302,7 @@ suite('ExtensionRecommendationsService Test', () => { function testNoPromptForValidRecommendations(recommendations: string[]) { return setUpFolderWorkspace('myFolder', recommendations).then(() => { testObject = instantiationService.createInstance(ExtensionRecommendationsService); - return testObject.loadWorkspaceConfigPromise.then(() => { + return testObject.activationPromise.then(() => { assert.equal(Object.keys(testObject.getAllRecommendationsWithReason()).length, recommendations.length); assert.ok(!prompted); }); @@ -341,7 +341,7 @@ suite('ExtensionRecommendationsService Test', () => { test('ExtensionRecommendationsService: Prompt for valid workspace recommendations', () => { return setUpFolderWorkspace('myFolder', mockTestData.recommendedExtensions).then(() => { testObject = instantiationService.createInstance(ExtensionRecommendationsService); - return testObject.loadWorkspaceConfigPromise.then(() => { + return testObject.activationPromise.then(() => { const recommendations = Object.keys(testObject.getAllRecommendationsWithReason()); assert.equal(recommendations.length, mockTestData.validRecommendedExtensions.length); @@ -373,7 +373,7 @@ suite('ExtensionRecommendationsService Test', () => { testConfigurationService.setUserConfiguration(ConfigurationKey, { showRecommendationsOnlyOnDemand: true }); return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { testObject = instantiationService.createInstance(ExtensionRecommendationsService); - return testObject.loadWorkspaceConfigPromise.then(() => { + return testObject.activationPromise.then(() => { assert.ok(!prompted); }); }); @@ -391,7 +391,7 @@ suite('ExtensionRecommendationsService Test', () => { return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { testObject = instantiationService.createInstance(ExtensionRecommendationsService); - return testObject.loadWorkspaceConfigPromise.then(() => { + return testObject.activationPromise.then(() => { const recommendations = testObject.getAllRecommendationsWithReason(); assert.ok(!recommendations['ms-dotnettools.csharp']); // stored recommendation that has been globally ignored assert.ok(recommendations['ms-python.python']); // stored recommendation @@ -409,7 +409,7 @@ suite('ExtensionRecommendationsService Test', () => { return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions, ignoredRecommendations).then(() => { testObject = instantiationService.createInstance(ExtensionRecommendationsService); - return testObject.loadWorkspaceConfigPromise.then(() => { + return testObject.activationPromise.then(() => { const recommendations = testObject.getAllRecommendationsWithReason(); assert.ok(!recommendations['ms-dotnettools.csharp']); // stored recommendation that has been workspace ignored assert.ok(recommendations['ms-python.python']); // stored recommendation @@ -430,7 +430,7 @@ suite('ExtensionRecommendationsService Test', () => { return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions, workspaceIgnoredRecommendations).then(() => { testObject = instantiationService.createInstance(ExtensionRecommendationsService); - return testObject.loadWorkspaceConfigPromise.then(() => { + return testObject.activationPromise.then(() => { const recommendations = testObject.getAllRecommendationsWithReason(); assert.ok(recommendations['ms-python.python']); @@ -449,7 +449,7 @@ suite('ExtensionRecommendationsService Test', () => { return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { testObject = instantiationService.createInstance(ExtensionRecommendationsService); - return testObject.loadWorkspaceConfigPromise.then(() => { + return testObject.activationPromise.then(() => { const recommendations = testObject.getAllRecommendationsWithReason(); assert.ok(recommendations['ms-python.python']); assert.ok(recommendations['mockpublisher1.mockextension1']); @@ -486,7 +486,7 @@ suite('ExtensionRecommendationsService Test', () => { testObject = instantiationService.createInstance(ExtensionRecommendationsService); testObject.onRecommendationChange(changeHandlerTarget); testObject.toggleIgnoredRecommendation(ignoredExtensionId, true); - await testObject.loadWorkspaceConfigPromise; + await testObject.activationPromise; assert.ok(changeHandlerTarget.calledOnce); assert.ok(changeHandlerTarget.getCall(0).calledWithMatch({ extensionId: ignoredExtensionId.toLowerCase(), isRecommended: false })); @@ -498,7 +498,7 @@ suite('ExtensionRecommendationsService Test', () => { return setUpFolderWorkspace('myFolder', []).then(() => { testObject = instantiationService.createInstance(ExtensionRecommendationsService); - return testObject.loadWorkspaceConfigPromise.then(() => { + return testObject.activationPromise.then(() => { const recommendations = testObject.getFileBasedRecommendations(); assert.equal(recommendations.length, 2); assert.ok(recommendations.some(({ extensionId }) => extensionId === 'ms-dotnettools.csharp')); // stored recommendation that exists in product.extensionTips @@ -517,7 +517,7 @@ suite('ExtensionRecommendationsService Test', () => { return setUpFolderWorkspace('myFolder', []).then(() => { testObject = instantiationService.createInstance(ExtensionRecommendationsService); - return testObject.loadWorkspaceConfigPromise.then(() => { + return testObject.activationPromise.then(() => { const recommendations = testObject.getFileBasedRecommendations(); assert.equal(recommendations.length, 2); assert.ok(recommendations.some(({ extensionId }) => extensionId === 'ms-dotnettools.csharp')); // stored recommendation that exists in product.extensionTips From 0681925277df77878322d2ad789b746f462a08c9 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 26 Aug 2020 12:29:16 +0200 Subject: [PATCH 535/736] refactor bulk edit service a little so that it is fit for new edit types, https://github.com/microsoft/vscode/issues/105283 --- .../browser/services/bulkEditService.ts | 58 ++++++++++- src/vs/editor/common/modes.ts | 24 ----- .../contrib/codeAction/codeActionCommands.ts | 4 +- src/vs/editor/contrib/rename/rename.ts | 4 +- .../standalone/browser/simpleServices.ts | 52 +++++----- .../api/browser/mainThreadEditors.ts | 4 +- .../bulkEdit/browser/bulkEditService.ts | 72 ++++++-------- .../contrib/bulkEdit/browser/bulkFileEdits.ts | 17 ++-- .../contrib/bulkEdit/browser/bulkTextEdits.ts | 35 ++++--- .../contrib/bulkEdit/browser/conflicts.ts | 28 +++--- .../browser/preview/bulkEdit.contribution.ts | 18 ++-- .../bulkEdit/browser/preview/bulkEditPane.ts | 10 +- .../browser/preview/bulkEditPreview.ts | 97 ++++++++++--------- .../bulkEdit/browser/preview/bulkEditTree.ts | 22 ++--- .../test/browser/bulkEditPreview.test.ts | 87 ++++++++--------- .../browser/contrib/format/formatting.ts | 18 ++-- .../browser/viewModel/notebookViewModel.ts | 9 +- .../contrib/search/browser/replaceService.ts | 29 +++--- 18 files changed, 296 insertions(+), 292 deletions(-) diff --git a/src/vs/editor/browser/services/bulkEditService.ts b/src/vs/editor/browser/services/bulkEditService.ts index 37393b426e3..7a1f3346e46 100644 --- a/src/vs/editor/browser/services/bulkEditService.ts +++ b/src/vs/editor/browser/services/bulkEditService.ts @@ -4,13 +4,64 @@ *--------------------------------------------------------------------------------------------*/ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { WorkspaceEdit } from 'vs/editor/common/modes'; +import { TextEdit, WorkspaceEdit, WorkspaceEditMetadata, WorkspaceFileEdit, WorkspaceFileEditOptions, WorkspaceTextEdit } from 'vs/editor/common/modes'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { isObject } from 'vs/base/common/types'; export const IBulkEditService = createDecorator('IWorkspaceEditService'); +function isWorkspaceFileEdit(thing: any): thing is WorkspaceFileEdit { + return isObject(thing) && (Boolean((thing).newUri) || Boolean((thing).oldUri)); +} + +function isWorkspaceTextEdit(thing: any): thing is WorkspaceTextEdit { + return isObject(thing) && URI.isUri((thing).resource) && isObject((thing).edit); +} + +export class ResourceEdit { + + protected constructor(readonly metadata?: WorkspaceEditMetadata) { } + + static convert(edit: WorkspaceEdit): ResourceEdit[] { + + + return edit.edits.map(edit => { + if (isWorkspaceTextEdit(edit)) { + return new ResourceTextEdit(edit.resource, edit.edit, edit.modelVersionId, edit.metadata); + } + if (isWorkspaceFileEdit(edit)) { + return new ResourceFileEdit(edit.oldUri, edit.newUri, edit.options, edit.metadata); + } + throw new Error('Unsupported edit'); + }); + } +} + +export class ResourceTextEdit extends ResourceEdit { + constructor( + readonly resource: URI, + readonly textEdit: TextEdit, + readonly versionId?: number, + readonly metadata?: WorkspaceEditMetadata + ) { + super(metadata); + } +} + +export class ResourceFileEdit extends ResourceEdit { + constructor( + readonly oldResource: URI | undefined, + readonly newResource: URI | undefined, + readonly options?: WorkspaceFileEditOptions, + readonly metadata?: WorkspaceEditMetadata + ) { + super(metadata); + } +} + export interface IBulkEditOptions { editor?: ICodeEditor; progress?: IProgress; @@ -23,7 +74,7 @@ export interface IBulkEditResult { ariaSummary: string; } -export type IBulkEditPreviewHandler = (edit: WorkspaceEdit, options?: IBulkEditOptions) => Promise; +export type IBulkEditPreviewHandler = (edits: ResourceEdit[], options?: IBulkEditOptions) => Promise; export interface IBulkEditService { readonly _serviceBrand: undefined; @@ -32,6 +83,5 @@ export interface IBulkEditService { setPreviewHandler(handler: IBulkEditPreviewHandler): IDisposable; - apply(edit: WorkspaceEdit, options?: IBulkEditOptions): Promise; + apply(edit: ResourceEdit[], options?: IBulkEditOptions): Promise; } - diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 578e369ec6f..3d34f627b1c 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -8,7 +8,6 @@ import { Color } from 'vs/base/common/color'; import { Event } from 'vs/base/common/event'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { isObject } from 'vs/base/common/types'; import { URI, UriComponents } from 'vs/base/common/uri'; import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; @@ -1337,29 +1336,6 @@ export class FoldingRangeKind { } } -/** - * @internal - */ -export namespace WorkspaceFileEdit { - /** - * @internal - */ - export function is(thing: any): thing is WorkspaceFileEdit { - return isObject(thing) && (Boolean((thing).newUri) || Boolean((thing).oldUri)); - } -} - -/** - * @internal - */ -export namespace WorkspaceTextEdit { - /** - * @internal - */ - export function is(thing: any): thing is WorkspaceTextEdit { - return isObject(thing) && URI.isUri((thing).resource) && isObject((thing).edit); - } -} export interface WorkspaceEditMetadata { needsConfirmation: boolean; diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 4acfb917796..699e05a4699 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -11,7 +11,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { escapeRegExpCharacters } from 'vs/base/common/strings'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, EditorCommand, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; +import { IBulkEditService, ResourceEdit } from 'vs/editor/browser/services/bulkEditService'; import { IPosition } from 'vs/editor/common/core/position'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; @@ -163,7 +163,7 @@ export async function applyCodeAction( }); if (action.edit) { - await bulkEditService.apply(action.edit, { editor, label: action.title }); + await bulkEditService.apply(ResourceEdit.convert(action.edit), { editor, label: action.title }); } if (action.command) { diff --git a/src/vs/editor/contrib/rename/rename.ts b/src/vs/editor/contrib/rename/rename.ts index 7ae883bcd2e..214d00ffc63 100644 --- a/src/vs/editor/contrib/rename/rename.ts +++ b/src/vs/editor/contrib/rename/rename.ts @@ -22,7 +22,7 @@ import { MessageController } from 'vs/editor/contrib/message/messageController'; import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from 'vs/editor/browser/core/editorState'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; +import { IBulkEditService, ResourceEdit } from 'vs/editor/browser/services/bulkEditService'; import { URI } from 'vs/base/common/uri'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; @@ -226,7 +226,7 @@ class RenameController implements IEditorContribution { return; } - this._bulkEditService.apply(renameResult, { + this._bulkEditService.apply(ResourceEdit.convert(renameResult), { editor: this.editor, showPreview: inputFieldResult.wantsPreview, label: nls.localize('label', "Renaming '{0}'", loc?.text), diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 9051fb2cc78..01554deb137 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -13,14 +13,13 @@ import { OS, isLinux, isMacintosh } from 'vs/base/common/platform'; import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor, IDiffEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { IBulkEditOptions, IBulkEditResult, IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; +import { IBulkEditOptions, IBulkEditResult, IBulkEditService, ResourceEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; import { isDiffEditorConfigurationKey, isEditorConfigurationKey } from 'vs/editor/common/config/commonEditorConfig'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { IPosition, Position as Pos } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { IEditor } from 'vs/editor/common/editorCommon'; -import { ITextModel, ITextSnapshot } from 'vs/editor/common/model'; -import { TextEdit, WorkspaceEdit, WorkspaceTextEdit } from 'vs/editor/common/modes'; +import { IIdentifiedSingleEditOperation, ITextModel, ITextSnapshot } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IResolvedTextEditorModel, ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService'; import { ITextResourceConfigurationService, ITextResourcePropertiesService, ITextResourceConfigurationChangeEvent } from 'vs/editor/common/services/textResourceConfigurationService'; @@ -665,42 +664,43 @@ export class SimpleBulkEditService implements IBulkEditService { return Disposable.None; } - apply(workspaceEdit: WorkspaceEdit, options?: IBulkEditOptions): Promise { + async apply(edits: ResourceEdit[], _options?: IBulkEditOptions): Promise { - let edits = new Map(); + const textEdits = new Map(); - if (workspaceEdit.edits) { - for (let edit of workspaceEdit.edits) { - if (!WorkspaceTextEdit.is(edit)) { - return Promise.reject(new Error('bad edit - only text edits are supported')); - } - let model = this._modelService.getModel(edit.resource); - if (!model) { - return Promise.reject(new Error('bad edit - model not found')); - } - let array = edits.get(model); - if (!array) { - array = []; - edits.set(model, array); - } - array.push(edit.edit); + for (let edit of edits) { + if (!(edit instanceof ResourceTextEdit)) { + throw new Error('bad edit - only text edits are supported'); } + const model = this._modelService.getModel(edit.resource); + if (!model) { + throw new Error('bad edit - model not found'); + } + if (typeof edit.versionId === 'number' && model.getVersionId() !== edit.versionId) { + throw new Error('bad state - model changed in the meantime'); + } + let array = textEdits.get(model); + if (!array) { + array = []; + textEdits.set(model, array); + } + array.push(EditOperation.replaceMove(Range.lift(edit.textEdit.range), edit.textEdit.text)); } + let totalEdits = 0; let totalFiles = 0; - edits.forEach((edits, model) => { + for (const [model, edits] of textEdits) { model.pushStackElement(); - model.pushEditOperations([], edits.map((e) => EditOperation.replaceMove(Range.lift(e.range), e.text)), () => []); + model.pushEditOperations([], edits, () => []); model.pushStackElement(); totalFiles += 1; totalEdits += edits.length; - }); + } - return Promise.resolve({ - selection: undefined, + return { ariaSummary: strings.format(SimpleServicesNLS.bulkEditServiceSummary, totalEdits, totalFiles) - }); + }; } } diff --git a/src/vs/workbench/api/browser/mainThreadEditors.ts b/src/vs/workbench/api/browser/mainThreadEditors.ts index ed1f0d0efc4..bdcc94c864e 100644 --- a/src/vs/workbench/api/browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadEditors.ts @@ -8,7 +8,7 @@ import { disposed } from 'vs/base/common/errors'; import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle'; import { equals as objectEquals } from 'vs/base/common/objects'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; +import { IBulkEditService, ResourceEdit } from 'vs/editor/browser/services/bulkEditService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IRange } from 'vs/editor/common/core/range'; import { ISelection } from 'vs/editor/common/core/selection'; @@ -223,7 +223,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { $tryApplyWorkspaceEdit(dto: IWorkspaceEditDto): Promise { const { edits } = reviveWorkspaceEditDto(dto)!; - return this._bulkEditService.apply({ edits }).then(() => true, _err => false); + return this._bulkEditService.apply(ResourceEdit.convert({ edits })).then(() => true, _err => false); } $tryInsertSnippet(id: string, template: string, ranges: readonly IRange[], opts: IUndoStopOptions): Promise { diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts index 70788d8f7e6..5900948f8b4 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts @@ -6,8 +6,7 @@ import { localize } from 'vs/nls'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { IBulkEditOptions, IBulkEditResult, IBulkEditService, IBulkEditPreviewHandler } from 'vs/editor/browser/services/bulkEditService'; -import { WorkspaceFileEdit, WorkspaceTextEdit, WorkspaceEdit } from 'vs/editor/common/modes'; +import { IBulkEditOptions, IBulkEditResult, IBulkEditService, IBulkEditPreviewHandler, ResourceEdit, ResourceFileEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { IProgress, IProgressStep, Progress } from 'vs/platform/progress/common/progress'; @@ -16,14 +15,11 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { BulkTextEdits } from 'vs/workbench/contrib/bulkEdit/browser/bulkTextEdits'; import { BulkFileEdits } from 'vs/workbench/contrib/bulkEdit/browser/bulkFileEdits'; -import { ResourceMap } from 'vs/base/common/map'; - -type Edit = WorkspaceFileEdit | WorkspaceTextEdit; class BulkEdit { private readonly _label: string | undefined; - private readonly _edits: Edit[] = []; + private readonly _edits: ResourceEdit[] = []; private readonly _editor: ICodeEditor | undefined; private readonly _progress: IProgress; @@ -31,7 +27,7 @@ class BulkEdit { label: string | undefined, editor: ICodeEditor | undefined, progress: IProgress | undefined, - edits: Edit[], + edits: ResourceEdit[], @IInstantiationService private readonly _instaService: IInstantiationService, @ILogService private readonly _logService: ILogService, ) { @@ -55,52 +51,44 @@ class BulkEdit { async perform(): Promise { - let seen = new ResourceMap(); - let total = 0; + if (this._edits.length === 0) { + return; + } - const groups: Edit[][] = []; - let group: Edit[] | undefined; - for (const edit of this._edits) { - if (!group - || (WorkspaceFileEdit.is(group[0]) && !WorkspaceFileEdit.is(edit)) - || (WorkspaceTextEdit.is(group[0]) && !WorkspaceTextEdit.is(edit)) - ) { - group = []; - groups.push(group); - } - group.push(edit); - - if (WorkspaceFileEdit.is(edit)) { - total += 1; - } else if (!seen.has(edit.resource)) { - seen.set(edit.resource, true); - total += 2; + const ranges: number[] = [1]; + for (let i = 1; i < this._edits.length; i++) { + if (Object.getPrototypeOf(this._edits[i - 1]) === Object.getPrototypeOf(this._edits[i])) { + ranges[ranges.length - 1]++; + } else { + ranges.push(1); } } - // define total work and progress callback - // for child operations - this._progress.report({ total }); - + this._progress.report({ total: this._edits.length }); const progress: IProgress = { report: _ => this._progress.report({ increment: 1 }) }; - // do it. - for (const group of groups) { - if (WorkspaceFileEdit.is(group[0])) { - await this._performFileEdits(group, progress); + + let index = 0; + for (let range of ranges) { + const group = this._edits.slice(index, index + range); + if (group[0] instanceof ResourceFileEdit) { + await this._performFileEdits(group, progress); + } else if (group[0] instanceof ResourceTextEdit) { + await this._performTextEdits(group, progress); } else { - await this._performTextEdits(group, progress); + console.log('UNKNOWN EDIT'); } + index = index + range; } } - private async _performFileEdits(edits: WorkspaceFileEdit[], progress: IProgress) { + private async _performFileEdits(edits: ResourceFileEdit[], progress: IProgress) { this._logService.debug('_performFileEdits', JSON.stringify(edits)); const model = this._instaService.createInstance(BulkFileEdits, this._label || localize('workspaceEdit', "Workspace Edit"), progress, edits); await model.apply(); } - private async _performTextEdits(edits: WorkspaceTextEdit[], progress: IProgress): Promise { + private async _performTextEdits(edits: ResourceTextEdit[], progress: IProgress): Promise { this._logService.debug('_performTextEdits', JSON.stringify(edits)); const model = this._instaService.createInstance(BulkTextEdits, this._label || localize('workspaceEdit', "Workspace Edit"), this._editor, progress, edits); await model.apply(); @@ -132,17 +120,17 @@ export class BulkEditService implements IBulkEditService { return Boolean(this._previewHandler); } - async apply(edit: WorkspaceEdit, options?: IBulkEditOptions): Promise { + async apply(edits: ResourceEdit[], options?: IBulkEditOptions): Promise { - if (edit.edits.length === 0) { + if (edits.length === 0) { return { ariaSummary: localize('nothing', "Made no edits") }; } - if (this._previewHandler && (options?.showPreview || edit.edits.some(value => value.metadata?.needsConfirmation))) { - edit = await this._previewHandler(edit, options); + if (this._previewHandler && (options?.showPreview || edits.some(value => value.metadata?.needsConfirmation))) { + edits = await this._previewHandler(edits, options); } - const { edits } = edit; + // const { edits } = edit; let codeEditor = options?.editor; // try to find code editor if (!codeEditor) { diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts index b5e768349cb..2a6e2bc7d54 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ -import { WorkspaceFileEdit, WorkspaceFileEditOptions } from 'vs/editor/common/modes'; +import { WorkspaceFileEditOptions } from 'vs/editor/common/modes'; import { IFileService, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; import { IProgress } from 'vs/platform/progress/common/progress'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -14,6 +14,7 @@ import { URI } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { VSBuffer } from 'vs/base/common/buffer'; +import { ResourceFileEdit } from 'vs/editor/browser/services/bulkEditService'; interface IFileOperation { uris: URI[]; @@ -147,7 +148,7 @@ export class BulkFileEdits { constructor( private readonly _label: string, private readonly _progress: IProgress, - private readonly _edits: WorkspaceFileEdit[], + private readonly _edits: ResourceFileEdit[], @IInstantiationService private readonly _instaService: IInstantiationService, @IUndoRedoService private readonly _undoRedoService: IUndoRedoService, ) { } @@ -159,15 +160,15 @@ export class BulkFileEdits { const options = edit.options || {}; let op: IFileOperation | undefined; - if (edit.newUri && edit.oldUri) { + if (edit.newResource && edit.oldResource) { // rename - op = this._instaService.createInstance(RenameOperation, edit.newUri, edit.oldUri, options); - } else if (!edit.newUri && edit.oldUri) { + op = this._instaService.createInstance(RenameOperation, edit.newResource, edit.oldResource, options); + } else if (!edit.newResource && edit.oldResource) { // delete file - op = this._instaService.createInstance(DeleteOperation, edit.oldUri, options); - } else if (edit.newUri && !edit.oldUri) { + op = this._instaService.createInstance(DeleteOperation, edit.oldResource, options); + } else if (edit.newResource && !edit.oldResource) { // create file - op = this._instaService.createInstance(CreateOperation, edit.newUri, options, undefined); + op = this._instaService.createInstance(CreateOperation, edit.newResource, options, undefined); } if (op) { const undoOp = await op.perform(); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts index ca9dfa7739c..1877843f01a 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts @@ -11,7 +11,6 @@ import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { EndOfLineSequence, IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; -import { WorkspaceTextEdit } from 'vs/editor/common/modes'; import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; import { IProgress } from 'vs/platform/progress/common/progress'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; @@ -19,6 +18,7 @@ import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { SingleModelEditStackElement, MultiModelEditStackElement } from 'vs/editor/common/model/editStack'; import { ResourceMap } from 'vs/base/common/map'; import { IModelService } from 'vs/editor/common/services/modelService'; +import { ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; type ValidationResult = { canApply: true } | { canApply: false, reason: URI }; @@ -39,31 +39,31 @@ class ModelEditTask implements IDisposable { this._modelReference.dispose(); } - addEdit(resourceEdit: WorkspaceTextEdit): void { - this._expectedModelVersionId = resourceEdit.modelVersionId; - const { edit } = resourceEdit; + addEdit(resourceEdit: ResourceTextEdit): void { + this._expectedModelVersionId = resourceEdit.versionId; + const { textEdit } = resourceEdit; - if (typeof edit.eol === 'number') { + if (typeof textEdit.eol === 'number') { // honor eol-change - this._newEol = edit.eol; + this._newEol = textEdit.eol; } - if (!edit.range && !edit.text) { + if (!textEdit.range && !textEdit.text) { // lacks both a range and the text return; } - if (Range.isEmpty(edit.range) && !edit.text) { + if (Range.isEmpty(textEdit.range) && !textEdit.text) { // no-op edit (replace empty range with empty text) return; } // create edit operation let range: Range; - if (!edit.range) { + if (!textEdit.range) { range = this.model.getFullModelRange(); } else { - range = Range.lift(edit.range); + range = Range.lift(textEdit.range); } - this._edits.push(EditOperation.replaceMove(range, edit.text)); + this._edits.push(EditOperation.replaceMove(range, textEdit.text)); } validate(): ValidationResult { @@ -116,13 +116,13 @@ class EditorEditTask extends ModelEditTask { export class BulkTextEdits { - private readonly _edits = new ResourceMap(); + private readonly _edits = new ResourceMap(); constructor( private readonly _label: string, private readonly _editor: ICodeEditor | undefined, private readonly _progress: IProgress, - edits: WorkspaceTextEdit[], + edits: ResourceTextEdit[], @IEditorWorkerService private readonly _editorWorker: IEditorWorkerService, @IModelService private readonly _modelService: IModelService, @ITextModelService private readonly _textModelResolverService: ITextModelService, @@ -143,9 +143,9 @@ export class BulkTextEdits { // First check if loaded models were not changed in the meantime for (const array of this._edits.values()) { for (let edit of array) { - if (typeof edit.modelVersionId === 'number') { + if (typeof edit.versionId === 'number') { let model = this._modelService.getModel(edit.resource); - if (model && model.getVersionId() !== edit.modelVersionId) { + if (model && model.getVersionId() !== edit.versionId) { // model changed in the meantime throw new Error(`${model.uri.toString()} has changed in the meantime`); } @@ -172,12 +172,12 @@ export class BulkTextEdits { for (const edit of value) { if (makeMinimal) { - const newEdits = await this._editorWorker.computeMoreMinimalEdits(edit.resource, [edit.edit]); + const newEdits = await this._editorWorker.computeMoreMinimalEdits(edit.resource, [edit.textEdit]); if (!newEdits) { task.addEdit(edit); } else { for (let moreMinialEdit of newEdits) { - task.addEdit({ ...edit, edit: moreMinialEdit }); + task.addEdit(new ResourceTextEdit(edit.resource, moreMinialEdit, edit.versionId, edit.metadata)); } } } else { @@ -186,7 +186,6 @@ export class BulkTextEdits { } tasks.push(task); - this._progress.report(undefined); }); promises.push(promise); } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/conflicts.ts b/src/vs/workbench/contrib/bulkEdit/browser/conflicts.ts index 341353d2adb..2a3ed128b4a 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/conflicts.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/conflicts.ts @@ -5,12 +5,12 @@ import { IFileService } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; -import { WorkspaceEdit, WorkspaceTextEdit } from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ResourceMap } from 'vs/base/common/map'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; import { ITextModel } from 'vs/editor/common/model'; +import { ResourceEdit, ResourceFileEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; export class ConflictDetector { @@ -21,31 +21,35 @@ export class ConflictDetector { readonly onDidConflict: Event = this._onDidConflict.event; constructor( - workspaceEdit: WorkspaceEdit, + edits: ResourceEdit[], @IFileService fileService: IFileService, @IModelService modelService: IModelService, ) { const _workspaceEditResources = new ResourceMap(); - for (let edit of workspaceEdit.edits) { - if (WorkspaceTextEdit.is(edit)) { - + for (let edit of edits) { + if (edit instanceof ResourceTextEdit) { _workspaceEditResources.set(edit.resource, true); - - if (typeof edit.modelVersionId === 'number') { + if (typeof edit.versionId === 'number') { const model = modelService.getModel(edit.resource); - if (model && model.getVersionId() !== edit.modelVersionId) { + if (model && model.getVersionId() !== edit.versionId) { this._conflicts.set(edit.resource, true); this._onDidConflict.fire(this); } } - } else if (edit.newUri) { - _workspaceEditResources.set(edit.newUri, true); + } else if (edit instanceof ResourceFileEdit) { + if (edit.newResource) { + _workspaceEditResources.set(edit.newResource, true); - } else if (edit.oldUri) { - _workspaceEditResources.set(edit.oldUri, true); + } else if (edit.oldResource) { + _workspaceEditResources.set(edit.oldResource, true); + } + + } else { + //todo@jrieken + console.log('UNKNOWN EDIT TYPE'); } } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts index 01159340519..de1fcca2f2d 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts @@ -7,8 +7,7 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; -import { WorkspaceEdit } from 'vs/editor/common/modes'; +import { IBulkEditService, ResourceEdit } from 'vs/editor/browser/services/bulkEditService'; import { BulkEditPane } from 'vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane'; import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainerLocation, IViewsRegistry, FocusedViewContext, IViewsService } from 'vs/workbench/common/views'; import { localize } from 'vs/nls'; @@ -105,18 +104,18 @@ class BulkEditPreviewContribution { @IBulkEditService bulkEditService: IBulkEditService, @IContextKeyService contextKeyService: IContextKeyService, ) { - bulkEditService.setPreviewHandler((edit) => this._previewEdit(edit)); + bulkEditService.setPreviewHandler(edits => this._previewEdit(edits)); this._ctxEnabled = BulkEditPreviewContribution.ctxEnabled.bindTo(contextKeyService); } - private async _previewEdit(edit: WorkspaceEdit) { + private async _previewEdit(edits: ResourceEdit[]): Promise { this._ctxEnabled.set(true); const uxState = this._activeSession?.uxState ?? new UXState(this._panelService, this._editorGroupsService); const view = await getBulkEditPane(this._viewsService); if (!view) { this._ctxEnabled.set(false); - return edit; + return edits; } // check for active preview session and let the user decide @@ -130,7 +129,7 @@ class BulkEditPreviewContribution { if (choice.choice === 0) { // this refactoring is being cancelled - return { edits: [] }; + return []; } } @@ -147,12 +146,7 @@ class BulkEditPreviewContribution { // the actual work... try { - const newEditOrUndefined = await view.setInput(edit, session.cts.token); - if (!newEditOrUndefined) { - return { edits: [] }; - } - - return newEditOrUndefined; + return await view.setInput(edits, session.cts.token); } finally { // restore UX state diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts index 395454875aa..64b406d9e07 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts @@ -5,7 +5,6 @@ import 'vs/css!./bulkEdit'; import { WorkbenchAsyncDataTree, IOpenEvent } from 'vs/platform/list/browser/listService'; -import { WorkspaceEdit } from 'vs/editor/common/modes'; import { BulkEditElement, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider, FileElement, TextEditElement, BulkEditAccessibilityProvider, CategoryElementRenderer, BulkEditNaviLabelProvider, CategoryElement, BulkEditSorter } from 'vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree'; import { FuzzyScore } from 'vs/base/common/filters'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -39,6 +38,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ResourceEdit } from 'vs/editor/browser/services/bulkEditService'; const enum State { Data = 'data', @@ -66,7 +66,7 @@ export class BulkEditPane extends ViewPane { private readonly _disposables = new DisposableStore(); private readonly _sessionDisposables = new DisposableStore(); - private _currentResolve?: (edit?: WorkspaceEdit) => void; + private _currentResolve?: (edit?: ResourceEdit[]) => void; private _currentInput?: BulkFileOperations; @@ -163,7 +163,7 @@ export class BulkEditPane extends ViewPane { this.element.dataset['state'] = state; } - async setInput(edit: WorkspaceEdit, token: CancellationToken): Promise { + async setInput(edit: ResourceEdit[], token: CancellationToken): Promise { this._setState(State.Data); this._sessionDisposables.clear(); this._treeViewStates.clear(); @@ -307,11 +307,11 @@ export class BulkEditPane extends ViewPane { let fileElement: FileElement; if (e.element instanceof TextEditElement) { fileElement = e.element.parent; - options.selection = e.element.edit.textEdit.edit.range; + options.selection = e.element.edit.textEdit.textEdit.range; } else if (e.element instanceof FileElement) { fileElement = e.element; - options.selection = e.element.edit.textEdits[0]?.textEdit.edit.range; + options.selection = e.element.edit.textEdits[0]?.textEdit.textEdit.range; } else { // invalid event diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts index 592b4872e08..4aecbf746cf 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts @@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; -import { WorkspaceEdit, WorkspaceTextEdit, WorkspaceFileEdit, WorkspaceEditMetadata } from 'vs/editor/common/modes'; +import { WorkspaceEditMetadata } from 'vs/editor/common/modes'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { mergeSort, coalesceInPlace } from 'vs/base/common/arrays'; import { Range } from 'vs/editor/common/core/range'; @@ -21,6 +21,7 @@ import { ConflictDetector } from 'vs/workbench/contrib/bulkEdit/browser/conflict import { ResourceMap } from 'vs/base/common/map'; import { localize } from 'vs/nls'; import { extUri } from 'vs/base/common/resources'; +import { ResourceEdit, ResourceFileEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; export class CheckedStates { @@ -67,7 +68,7 @@ export class BulkTextEdit { constructor( readonly parent: BulkFileOperation, - readonly textEdit: WorkspaceTextEdit + readonly textEdit: ResourceTextEdit ) { } } @@ -82,7 +83,7 @@ export class BulkFileOperation { type: BulkFileOperationType = 0; textEdits: BulkTextEdit[] = []; - originalEdits = new Map(); + originalEdits = new Map(); newUri?: URI; constructor( @@ -90,14 +91,14 @@ export class BulkFileOperation { readonly parent: BulkFileOperations ) { } - addEdit(index: number, type: BulkFileOperationType, edit: WorkspaceTextEdit | WorkspaceFileEdit) { + addEdit(index: number, type: BulkFileOperationType, edit: ResourceTextEdit | ResourceFileEdit) { this.type |= type; this.originalEdits.set(index, edit); - if (WorkspaceTextEdit.is(edit)) { + if (edit instanceof ResourceTextEdit) { this.textEdits.push(new BulkTextEdit(this, edit)); } else if (type === BulkFileOperationType.Rename) { - this.newUri = edit.newUri; + this.newUri = edit.newResource; } } @@ -134,19 +135,19 @@ export class BulkCategory { export class BulkFileOperations { - static async create(accessor: ServicesAccessor, bulkEdit: WorkspaceEdit): Promise { + static async create(accessor: ServicesAccessor, bulkEdit: ResourceEdit[]): Promise { const result = accessor.get(IInstantiationService).createInstance(BulkFileOperations, bulkEdit); return await result._init(); } - readonly checked = new CheckedStates(); + readonly checked = new CheckedStates(); readonly fileOperations: BulkFileOperation[] = []; readonly categories: BulkCategory[] = []; readonly conflicts: ConflictDetector; constructor( - private readonly _bulkEdit: WorkspaceEdit, + private readonly _bulkEdit: ResourceEdit[], @IFileService private readonly _fileService: IFileService, @IInstantiationService instaService: IInstantiationService, ) { @@ -164,8 +165,8 @@ export class BulkFileOperations { const newToOldUri = new ResourceMap(); - for (let idx = 0; idx < this._bulkEdit.edits.length; idx++) { - const edit = this._bulkEdit.edits[idx]; + for (let idx = 0; idx < this._bulkEdit.length; idx++) { + const edit = this._bulkEdit[idx]; let uri: URI; let type: BulkFileOperationType; @@ -173,39 +174,45 @@ export class BulkFileOperations { // store inital checked state this.checked.updateChecked(edit, !edit.metadata?.needsConfirmation); - if (WorkspaceTextEdit.is(edit)) { + if (edit instanceof ResourceTextEdit) { type = BulkFileOperationType.TextEdit; uri = edit.resource; - } else if (edit.newUri && edit.oldUri) { - type = BulkFileOperationType.Rename; - uri = edit.oldUri; - if (edit.options?.overwrite === undefined && edit.options?.ignoreIfExists && await this._fileService.exists(uri)) { - // noop -> "soft" rename to something that already exists - continue; - } - // map newUri onto oldUri so that text-edit appear for - // the same file element - newToOldUri.set(edit.newUri, uri); + } else if (edit instanceof ResourceFileEdit) { + if (edit.newResource && edit.oldResource) { + type = BulkFileOperationType.Rename; + uri = edit.oldResource; + if (edit.options?.overwrite === undefined && edit.options?.ignoreIfExists && await this._fileService.exists(uri)) { + // noop -> "soft" rename to something that already exists + continue; + } + // map newResource onto oldResource so that text-edit appear for + // the same file element + newToOldUri.set(edit.newResource, uri); - } else if (edit.oldUri) { - type = BulkFileOperationType.Delete; - uri = edit.oldUri; - if (edit.options?.ignoreIfNotExists && !await this._fileService.exists(uri)) { - // noop -> "soft" delete something that doesn't exist - continue; - } + } else if (edit.oldResource) { + type = BulkFileOperationType.Delete; + uri = edit.oldResource; + if (edit.options?.ignoreIfNotExists && !await this._fileService.exists(uri)) { + // noop -> "soft" delete something that doesn't exist + continue; + } - } else if (edit.newUri) { - type = BulkFileOperationType.Create; - uri = edit.newUri; - if (edit.options?.overwrite === undefined && edit.options?.ignoreIfExists && await this._fileService.exists(uri)) { - // noop -> "soft" create something that already exists + } else if (edit.newResource) { + type = BulkFileOperationType.Create; + uri = edit.newResource; + if (edit.options?.overwrite === undefined && edit.options?.ignoreIfExists && await this._fileService.exists(uri)) { + // noop -> "soft" create something that already exists + continue; + } + + } else { + // invalid edit -> skip continue; } } else { - // invalid edit -> skip + // unsupported edit continue; } @@ -249,7 +256,7 @@ export class BulkFileOperations { if (file.type !== BulkFileOperationType.TextEdit) { let checked = true; for (const edit of file.originalEdits.values()) { - if (WorkspaceFileEdit.is(edit)) { + if (edit instanceof ResourceFileEdit) { checked = checked && this.checked.isChecked(edit); } } @@ -275,14 +282,14 @@ export class BulkFileOperations { return this; } - getWorkspaceEdit(): WorkspaceEdit { - const result: WorkspaceEdit = { edits: [] }; + getWorkspaceEdit(): ResourceEdit[] { + const result: ResourceEdit[] = []; let allAccepted = true; - for (let i = 0; i < this._bulkEdit.edits.length; i++) { - const edit = this._bulkEdit.edits[i]; + for (let i = 0; i < this._bulkEdit.length; i++) { + const edit = this._bulkEdit[i]; if (this.checked.isChecked(edit)) { - result.edits[i] = edit; + result[i] = edit; continue; } allAccepted = false; @@ -293,7 +300,7 @@ export class BulkFileOperations { } // not all edits have been accepted - coalesceInPlace(result.edits); + coalesceInPlace(result); return result; } @@ -306,9 +313,9 @@ export class BulkFileOperations { let ignoreAll = false; for (const edit of file.originalEdits.values()) { - if (WorkspaceTextEdit.is(edit)) { + if (edit instanceof ResourceTextEdit) { if (this.checked.isChecked(edit)) { - result.push(EditOperation.replaceMove(Range.lift(edit.edit.range), edit.edit.text)); + result.push(EditOperation.replaceMove(Range.lift(edit.textEdit.range), edit.textEdit.text)); } } else if (!this.checked.isChecked(edit)) { @@ -330,7 +337,7 @@ export class BulkFileOperations { return []; } - getUriOfEdit(edit: WorkspaceFileEdit | WorkspaceTextEdit): URI { + getUriOfEdit(edit: ResourceEdit): URI { for (let file of this.fileOperations) { for (const value of file.originalEdits.values()) { if (value === edit) { diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts index b9f669022cb..4db2acf28b7 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts @@ -22,11 +22,11 @@ import type { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWid import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { basename } from 'vs/base/common/resources'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { WorkspaceFileEdit } from 'vs/editor/common/modes'; import { compare } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { Iterable } from 'vs/base/common/iterator'; +import { ResourceFileEdit } from 'vs/editor/browser/services/bulkEditService'; // --- VIEW MODEL @@ -62,7 +62,7 @@ export class FileElement implements ICheckable { // multiple file edits -> reflect single state for (let edit of this.edit.originalEdits.values()) { - if (WorkspaceFileEdit.is(edit)) { + if (edit instanceof ResourceFileEdit) { checked = checked && model.checked.isChecked(edit); } } @@ -73,7 +73,7 @@ export class FileElement implements ICheckable { for (let file of category.fileOperations) { if (file.uri.toString() === this.edit.uri.toString()) { for (const edit of file.originalEdits.values()) { - if (WorkspaceFileEdit.is(edit)) { + if (edit instanceof ResourceFileEdit) { checked = checked && model.checked.isChecked(edit); } } @@ -113,7 +113,7 @@ export class FileElement implements ICheckable { for (let file of category.fileOperations) { if (file.uri.toString() === this.edit.uri.toString()) { for (const edit of file.originalEdits.values()) { - if (WorkspaceFileEdit.is(edit)) { + if (edit instanceof ResourceFileEdit) { checked = checked && model.checked.isChecked(edit); } } @@ -155,7 +155,7 @@ export class TextEditElement implements ICheckable { // make sure parent is checked when this element is checked... if (value) { for (const edit of this.parent.edit.originalEdits.values()) { - if (WorkspaceFileEdit.is(edit)) { + if (edit instanceof ResourceFileEdit) { (model).checked.updateChecked(edit, value); } } @@ -219,7 +219,7 @@ export class BulkEditDataSource implements IAsyncDataSource { - const range = Range.lift(edit.textEdit.edit.range); + const range = Range.lift(edit.textEdit.textEdit.range); //prefix-math let startTokens = textModel.getLineTokens(range.startLineNumber); @@ -241,7 +241,7 @@ export class BulkEditDataSource implements IAsyncDataSource { } if (a instanceof TextEditElement && b instanceof TextEditElement) { - return Range.compareRangesUsingStarts(a.edit.textEdit.edit.range, b.edit.textEdit.edit.range); + return Range.compareRangesUsingStarts(a.edit.textEdit.textEdit.range, b.edit.textEdit.textEdit.range); } return 0; @@ -336,13 +336,13 @@ export class BulkEditAccessibilityProvider implements IListAccessibilityProvider if (element instanceof TextEditElement) { if (element.selecting.length > 0 && element.inserting.length > 0) { // edit: replace - return localize('aria.replace', "line {0}, replacing {1} with {2}", element.edit.textEdit.edit.range.startLineNumber, element.selecting, element.inserting); + return localize('aria.replace', "line {0}, replacing {1} with {2}", element.edit.textEdit.textEdit.range.startLineNumber, element.selecting, element.inserting); } else if (element.selecting.length > 0 && element.inserting.length === 0) { // edit: delete - return localize('aria.del', "line {0}, removing {1}", element.edit.textEdit.edit.range.startLineNumber, element.selecting); + return localize('aria.del', "line {0}, removing {1}", element.edit.textEdit.textEdit.range.startLineNumber, element.selecting); } else if (element.selecting.length === 0 && element.inserting.length > 0) { // edit: insert - return localize('aria.insert', "line {0}, inserting {1}", element.edit.textEdit.edit.range.startLineNumber, element.selecting); + return localize('aria.insert', "line {0}, inserting {1}", element.edit.textEdit.textEdit.range.startLineNumber, element.selecting); } } diff --git a/src/vs/workbench/contrib/bulkEdit/test/browser/bulkEditPreview.test.ts b/src/vs/workbench/contrib/bulkEdit/test/browser/bulkEditPreview.test.ts index ebc38122e44..0aac694ea89 100644 --- a/src/vs/workbench/contrib/bulkEdit/test/browser/bulkEditPreview.test.ts +++ b/src/vs/workbench/contrib/bulkEdit/test/browser/bulkEditPreview.test.ts @@ -11,10 +11,10 @@ import { InstantiationService } from 'vs/platform/instantiation/common/instantia import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IModelService } from 'vs/editor/common/services/modelService'; -import type { WorkspaceEdit } from 'vs/editor/common/modes'; import { URI } from 'vs/base/common/uri'; import { BulkFileOperations } from 'vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview'; import { Range } from 'vs/editor/common/core/range'; +import { ResourceFileEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; suite('BulkEditPreview', function () { @@ -47,28 +47,25 @@ suite('BulkEditPreview', function () { test('one needsConfirmation unchecks all of file', async function () { - const edit: WorkspaceEdit = { - edits: [ - { newUri: URI.parse('some:///uri1'), metadata: { label: 'cat1', needsConfirmation: true } }, - { oldUri: URI.parse('some:///uri1'), newUri: URI.parse('some:///uri2'), metadata: { label: 'cat2', needsConfirmation: false } }, - ] - }; + const edits = [ + new ResourceFileEdit(undefined, URI.parse('some:///uri1'), undefined, { label: 'cat1', needsConfirmation: true }), + new ResourceFileEdit(URI.parse('some:///uri1'), URI.parse('some:///uri2'), undefined, { label: 'cat2', needsConfirmation: false }), + ]; - const ops = await instaService.invokeFunction(BulkFileOperations.create, edit); + const ops = await instaService.invokeFunction(BulkFileOperations.create, edits); assert.equal(ops.fileOperations.length, 1); - assert.equal(ops.checked.isChecked(edit.edits[0]), false); + assert.equal(ops.checked.isChecked(edits[0]), false); }); test('has categories', async function () { - const edit: WorkspaceEdit = { - edits: [ - { newUri: URI.parse('some:///uri1'), metadata: { label: 'uri1', needsConfirmation: true } }, - { newUri: URI.parse('some:///uri2'), metadata: { label: 'uri2', needsConfirmation: false } } - ] - }; + const edits = [ + new ResourceFileEdit(undefined, URI.parse('some:///uri1'), undefined, { label: 'uri1', needsConfirmation: true }), + new ResourceFileEdit(undefined, URI.parse('some:///uri2'), undefined, { label: 'uri2', needsConfirmation: false }), + ]; - const ops = await instaService.invokeFunction(BulkFileOperations.create, edit); + + const ops = await instaService.invokeFunction(BulkFileOperations.create, edits); assert.equal(ops.categories.length, 2); assert.equal(ops.categories[0].metadata.label, 'uri1'); // unconfirmed! assert.equal(ops.categories[1].metadata.label, 'uri2'); @@ -76,14 +73,12 @@ suite('BulkEditPreview', function () { test('has not categories', async function () { - const edit: WorkspaceEdit = { - edits: [ - { newUri: URI.parse('some:///uri1'), metadata: { label: 'uri1', needsConfirmation: true } }, - { newUri: URI.parse('some:///uri2'), metadata: { label: 'uri1', needsConfirmation: false } } - ] - }; + const edits = [ + new ResourceFileEdit(undefined, URI.parse('some:///uri1'), undefined, { label: 'uri1', needsConfirmation: true }), + new ResourceFileEdit(undefined, URI.parse('some:///uri2'), undefined, { label: 'uri1', needsConfirmation: false }), + ]; - const ops = await instaService.invokeFunction(BulkFileOperations.create, edit); + const ops = await instaService.invokeFunction(BulkFileOperations.create, edits); assert.equal(ops.categories.length, 1); assert.equal(ops.categories[0].metadata.label, 'uri1'); // unconfirmed! assert.equal(ops.categories[0].metadata.label, 'uri1'); @@ -91,43 +86,41 @@ suite('BulkEditPreview', function () { test('category selection', async function () { - const edit: WorkspaceEdit = { - edits: [ - { newUri: URI.parse('some:///uri1'), metadata: { label: 'C1', needsConfirmation: false } }, - { resource: URI.parse('some:///uri2'), edit: { text: 'foo', range: new Range(1, 1, 1, 1) }, metadata: { label: 'C2', needsConfirmation: false } } - ] - }; + const edits = [ + new ResourceFileEdit(undefined, URI.parse('some:///uri1'), undefined, { label: 'C1', needsConfirmation: false }), + new ResourceTextEdit(URI.parse('some:///uri2'), { text: 'foo', range: new Range(1, 1, 1, 1) }, undefined, { label: 'C2', needsConfirmation: false }), + ]; - const ops = await instaService.invokeFunction(BulkFileOperations.create, edit); - assert.equal(ops.checked.isChecked(edit.edits[0]), true); - assert.equal(ops.checked.isChecked(edit.edits[1]), true); + const ops = await instaService.invokeFunction(BulkFileOperations.create, edits); - assert.ok(edit === ops.getWorkspaceEdit()); + assert.equal(ops.checked.isChecked(edits[0]), true); + assert.equal(ops.checked.isChecked(edits[1]), true); + + assert.ok(edits === ops.getWorkspaceEdit()); // NOT taking to create, but the invalid text edit will // go through - ops.checked.updateChecked(edit.edits[0], false); - const newEdit = ops.getWorkspaceEdit(); - assert.ok(edit !== newEdit); + ops.checked.updateChecked(edits[0], false); + const newEdits = ops.getWorkspaceEdit(); + assert.ok(edits !== newEdits); - assert.equal(edit.edits.length, 2); - assert.equal(newEdit.edits.length, 1); + assert.equal(edits.length, 2); + assert.equal(newEdits.length, 1); }); test('fix bad metadata', async function () { // bogous edit that wants creation to be confirmed, but not it's textedit-child... - const edit: WorkspaceEdit = { - edits: [ - { newUri: URI.parse('some:///uri1'), metadata: { label: 'C1', needsConfirmation: true } }, - { resource: URI.parse('some:///uri1'), edit: { text: 'foo', range: new Range(1, 1, 1, 1) }, metadata: { label: 'C2', needsConfirmation: false } } - ] - }; - const ops = await instaService.invokeFunction(BulkFileOperations.create, edit); + const edits = [ + new ResourceFileEdit(undefined, URI.parse('some:///uri1'), undefined, { label: 'C1', needsConfirmation: true }), + new ResourceTextEdit(URI.parse('some:///uri1'), { text: 'foo', range: new Range(1, 1, 1, 1) }, undefined, { label: 'C2', needsConfirmation: false }) + ]; - assert.equal(ops.checked.isChecked(edit.edits[0]), false); - assert.equal(ops.checked.isChecked(edit.edits[1]), false); + const ops = await instaService.invokeFunction(BulkFileOperations.create, edits); + + assert.equal(ops.checked.isChecked(edits[0]), false); + assert.equal(ops.checked.isChecked(edits[1]), false); }); }); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/format/formatting.ts b/src/vs/workbench/contrib/notebook/browser/contrib/format/formatting.ts index 6cb8b4a2bb0..446f4940e2a 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/format/formatting.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/format/formatting.ts @@ -17,8 +17,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { getDocumentFormattingEditsUntilResult, formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; -import { WorkspaceTextEdit } from 'vs/editor/common/modes'; +import { IBulkEditService, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { registerEditorAction, EditorAction } from 'vs/editor/browser/editorExtensions'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -63,7 +62,7 @@ registerAction2(class extends Action2 { const dispoables = new DisposableStore(); try { - const edits: WorkspaceTextEdit[] = []; + const edits: ResourceTextEdit[] = []; for (const cell of notebook.cells) { @@ -78,18 +77,13 @@ registerAction2(class extends Action2 { ); if (formatEdits) { - formatEdits.forEach(edit => edits.push({ - edit, - resource: model.uri, - modelVersionId: model.getVersionId() - })); + for (let edit of formatEdits) { + edits.push(new ResourceTextEdit(model.uri, edit, model.getVersionId())); + } } } - await bulkEditService.apply( - { edits }, - { label: localize('label', "Format Notebook") } - ); + await bulkEditService.apply(edits, { label: localize('label', "Format Notebook") }); } finally { dispoables.dispose(); diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts index 033f378e21c..0c02d11cf09 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts @@ -8,7 +8,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; +import { IBulkEditService, ResourceEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; import { Range } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness, IReadonlyTextBuffer } from 'vs/editor/common/model'; @@ -1027,7 +1027,10 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD const viewCell = cell as CellViewModel; this._lastNotebookEditResource.push(viewCell.uri); return viewCell.resolveTextModel().then(() => { - this._bulkEditService.apply({ edits: [{ edit: { range: range, text: text }, resource: cell.uri }] }, { quotableLabel: 'Notebook Replace' }); + this._bulkEditService.apply( + [new ResourceTextEdit(cell.uri, { range, text })], + { quotableLabel: 'Notebook Replace' } + ); }); } @@ -1051,7 +1054,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD return Promise.all(matches.map(match => { return match.cell.resolveTextModel(); })).then(async () => { - this._bulkEditService.apply({ edits: textEdits }, { quotableLabel: 'Notebook Replace All' }); + this._bulkEditService.apply(ResourceEdit.convert({ edits: textEdits }), { quotableLabel: 'Notebook Replace All' }); return; }); } diff --git a/src/vs/workbench/contrib/search/browser/replaceService.ts b/src/vs/workbench/contrib/search/browser/replaceService.ts index 90023f40b24..ba13946ada5 100644 --- a/src/vs/workbench/contrib/search/browser/replaceService.ts +++ b/src/vs/workbench/contrib/search/browser/replaceService.ts @@ -18,10 +18,9 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { ITextModel, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { WorkspaceTextEdit } from 'vs/editor/common/modes'; import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; +import { IBulkEditService, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; import { Range } from 'vs/editor/common/core/range'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { mergeSort } from 'vs/base/common/arrays'; @@ -101,8 +100,8 @@ export class ReplaceService implements IReplaceService { replace(files: FileMatch[], progress?: IProgress): Promise; replace(match: FileMatchOrMatch, progress?: IProgress, resource?: URI): Promise; async replace(arg: any, progress: IProgress | undefined = undefined, resource: URI | null = null): Promise { - const edits: WorkspaceTextEdit[] = this.createEdits(arg, resource); - await this.bulkEditorService.apply({ edits }, { progress }); + const edits = this.createEdits(arg, resource); + await this.bulkEditorService.apply(edits, { progress }); return Promise.all(edits.map(e => this.textFileService.files.get(e.resource)?.save())); } @@ -162,15 +161,15 @@ export class ReplaceService implements IReplaceService { const modelEdits: IIdentifiedSingleEditOperation[] = []; for (const resourceEdit of resourceEdits) { modelEdits.push(EditOperation.replaceMove( - Range.lift(resourceEdit.edit.range), - resourceEdit.edit.text) + Range.lift(resourceEdit.textEdit.range), + resourceEdit.textEdit.text) ); } replaceModel.pushEditOperations([], mergeSort(modelEdits, (a, b) => Range.compareRangesUsingStarts(a.range, b.range)), () => []); } - private createEdits(arg: FileMatchOrMatch | FileMatch[], resource: URI | null = null): WorkspaceTextEdit[] { - const edits: WorkspaceTextEdit[] = []; + private createEdits(arg: FileMatchOrMatch | FileMatch[], resource: URI | null = null): ResourceTextEdit[] { + const edits: ResourceTextEdit[] = []; if (arg instanceof Match) { const match = arg; @@ -193,15 +192,11 @@ export class ReplaceService implements IReplaceService { return edits; } - private createEdit(match: Match, text: string, resource: URI | null = null): WorkspaceTextEdit { + private createEdit(match: Match, text: string, resource: URI | null = null): ResourceTextEdit { const fileMatch: FileMatch = match.parent(); - const resourceEdit: WorkspaceTextEdit = { - resource: resource !== null ? resource : fileMatch.resource, - edit: { - range: match.range(), - text: text - } - }; - return resourceEdit; + return new ResourceTextEdit( + resource ?? fileMatch.resource, + { range: match.range(), text }, undefined, undefined + ); } } From f922e8b7574608d7220d9c39d7f99edfc63c7b68 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 26 Aug 2020 13:07:30 +0200 Subject: [PATCH 536/736] debt - remove lib.array-ext.d.ts and support Array.includes (fix #102566) (#105404) --- src/tsconfig.base.json | 1 + src/tsconfig.monaco.json | 1 - src/typings/lib.array-ext.d.ts | 11 ----------- .../actions/browser/menuEntryActionViewItem.ts | 4 ++-- .../workbench/api/common/extHostLanguageFeatures.ts | 2 +- .../workbench/contrib/comments/browser/commentNode.ts | 4 ++-- .../extensions/browser/fileBasedRecommendations.ts | 2 +- .../notebook/browser/view/renderers/cellActionView.ts | 4 ++-- 8 files changed, 9 insertions(+), 20 deletions(-) delete mode 100644 src/typings/lib.array-ext.d.ts diff --git a/src/tsconfig.base.json b/src/tsconfig.base.json index a4c419cf5f4..6decaae056e 100644 --- a/src/tsconfig.base.json +++ b/src/tsconfig.base.json @@ -16,6 +16,7 @@ }, "lib": [ "ES2015", + "ES2016.Array.Include", "ES2017.String", "ES2018.Promise", "DOM", diff --git a/src/tsconfig.monaco.json b/src/tsconfig.monaco.json index 825a83761f2..86b2926a1ab 100644 --- a/src/tsconfig.monaco.json +++ b/src/tsconfig.monaco.json @@ -15,7 +15,6 @@ "include": [ "typings/require.d.ts", "typings/thenable.d.ts", - "typings/lib.array-ext.d.ts", "vs/css.d.ts", "vs/monaco.d.ts", "vs/nls.d.ts", diff --git a/src/typings/lib.array-ext.d.ts b/src/typings/lib.array-ext.d.ts deleted file mode 100644 index 5a77b70a9f2..00000000000 --- a/src/typings/lib.array-ext.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -interface ArrayConstructor { - isArray(arg: ReadonlyArray | null | undefined): arg is ReadonlyArray; - isArray(arg: Array | null | undefined): arg is Array; - isArray(arg: any): arg is Array; - isArray(arg: any): arg is Array; -} \ No newline at end of file diff --git a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts index 0c84bbc7c63..48b171e1b76 100644 --- a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts +++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts @@ -110,11 +110,11 @@ function fillInActions(groups: ReadonlyArray<[string, ReadonlyArray(target) ? target : target.primary; + const to = Array.isArray(target) ? target : target.primary; to.unshift(...actions); } else { - const to = Array.isArray(target) ? target : target.secondary; + const to = Array.isArray(target) ? target : target.secondary; if (to.length > 0) { to.push(new Separator()); diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index a76867f4531..c912e48141d 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1120,7 +1120,7 @@ class ColorProviderAdapter { provideColors(resource: URI, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); return asPromise(() => this._provider.provideDocumentColors(doc, token)).then(colors => { - if (!Array.isArray(colors)) { + if (!Array.isArray(colors)) { return []; } diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index ec11e213ac4..490a1a53d87 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -535,11 +535,11 @@ function fillInActions(groups: [string, Array(target) ? target : target.primary; + const to = Array.isArray(target) ? target : target.primary; to.unshift(...actions); } else { - const to = Array.isArray(target) ? target : target.secondary; + const to = Array.isArray(target) ? target : target.secondary; if (to.length > 0) { to.push(new Separator()); diff --git a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts index 1e0208da2aa..fda072b2b69 100644 --- a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts @@ -296,7 +296,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { private getCachedRecommendations(): IStringDictionary { let storedRecommendations = JSON.parse(this.storageService.get(recommendationsStorageKey, StorageScope.GLOBAL, '[]')); - if (Array.isArray(storedRecommendations)) { + if (Array.isArray(storedRecommendations)) { storedRecommendations = storedRecommendations.reduce((result, id) => { result[id] = Date.now(); return result; }, >{}); } const result: IStringDictionary = {}; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts index 69e4ddad204..66ffa090a93 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts @@ -45,7 +45,7 @@ function fillInActions(groups: ReadonlyArray<[string, ReadonlyArray(target) ? target : target.primary; + const to = Array.isArray(target) ? target : target.primary; if (to.length > 0) { to.push(new VerticalSeparator()); @@ -55,7 +55,7 @@ function fillInActions(groups: ReadonlyArray<[string, ReadonlyArray(target) ? target : target.secondary; + const to = Array.isArray(target) ? target : target.secondary; if (to.length > 0) { to.push(new Separator()); From 70da5e2710c8c981470041e3268b3396460dbbbb Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru Date: Wed, 26 Aug 2020 13:28:43 +0200 Subject: [PATCH 537/736] =?UTF-8?q?PoliCheck=20=F0=9F=92=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/vs/editor/browser/viewParts/selections/selections.ts | 4 ++-- .../workbench/contrib/terminal/browser/terminalInstance.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/browser/viewParts/selections/selections.ts b/src/vs/editor/browser/viewParts/selections/selections.ts index 47a977fe45c..cdb00570ce0 100644 --- a/src/vs/editor/browser/viewParts/selections/selections.ts +++ b/src/vs/editor/browser/viewParts/selections/selections.ts @@ -217,7 +217,7 @@ export class SelectionsOverlay extends DynamicViewOverlay { endStyle.top = CornerStyle.INTERN; } } else if (previousFrameTop) { - // Accept some hick-ups near the viewport edges to save on repaints + // Accept some hiccups near the viewport edges to save on repaints startStyle.top = previousFrameTop.startStyle!.top; endStyle.top = previousFrameTop.endStyle!.top; } @@ -239,7 +239,7 @@ export class SelectionsOverlay extends DynamicViewOverlay { endStyle.bottom = CornerStyle.INTERN; } } else if (previousFrameBottom) { - // Accept some hick-ups near the viewport edges to save on repaints + // Accept some hiccups near the viewport edges to save on repaints startStyle.bottom = previousFrameBottom.startStyle!.bottom; endStyle.bottom = previousFrameBottom.endStyle!.bottom; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 01fbd5bded8..7739e56ef72 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -1128,9 +1128,9 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { if (!reset) { // HACK: Force initialText to be non-falsy for reused terminals such that the - // conptyInheritCursor flag is passed to the node-pty, this flag can cause a Window to hang - // in Windows 10 1903 so we only want to use it when something is definitely written to the - // terminal. + // conptyInheritCursor flag is passed to the node-pty, this flag can cause a Window to stop + // responding in Windows 10 1903 so we only want to use it when something is definitely written + // to the terminal. shell.initialText = ' '; } From bbb9784d8bb47db5558b8bb4e3dedcf932472993 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru Date: Wed, 26 Aug 2020 14:42:39 +0200 Subject: [PATCH 538/736] =?UTF-8?q?PoliCheck=20=F0=9F=92=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- extensions/git/src/timelineProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/src/timelineProvider.ts b/extensions/git/src/timelineProvider.ts index 5600b6f0e0c..94857f8e210 100644 --- a/extensions/git/src/timelineProvider.ts +++ b/extensions/git/src/timelineProvider.ts @@ -232,7 +232,7 @@ export class GitTimelineProvider implements TimelineProvider { private onRepositoryStatusChanged(_repo: Repository) { // console.log(`GitTimelineProvider.onRepositoryStatusChanged`); - // This is crappy, but for now just save the last time a status was run and use that as the timestamp for staged items + // This is less than ideal, but for now just save the last time a status was run and use that as the timestamp for staged items this.repoStatusDate = new Date(); this.fireChanged(); From 5f80fba373f57bd5cae65f508949c7e27006f266 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 26 Aug 2020 14:47:43 +0200 Subject: [PATCH 539/736] fuzzy scorer - use prefix highlight positions if available --- src/vs/base/common/fuzzyScorer.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/base/common/fuzzyScorer.ts b/src/vs/base/common/fuzzyScorer.ts index ad53ee315be..dfb734d8e61 100644 --- a/src/vs/base/common/fuzzyScorer.ts +++ b/src/vs/base/common/fuzzyScorer.ts @@ -463,17 +463,20 @@ function doScoreItemFuzzySingle(label: string, description: string | undefined, if (preferLabelMatches || !description) { const [labelScore, labelPositions] = scoreFuzzy(label, query.normalized, query.normalizedLowercase, fuzzy); if (labelScore) { - // If we have a prefix match on the label, we give a much // higher baseScore to elevate these matches over others + // This ensures that typing a file name wins over results + // that are present somewhere in the label, but not the + // beginning. + const labelPrefixMatch = matchesPrefix(query.normalized, label); let baseScore: number; - if (matchesPrefix(query.normalized, label)) { + if (labelPrefixMatch) { baseScore = LABEL_PREFIX_SCORE_THRESHOLD; } else { baseScore = LABEL_SCORE_THRESHOLD; } - return { score: baseScore + labelScore, labelMatch: createMatches(labelPositions) }; + return { score: baseScore + labelScore, labelMatch: labelPrefixMatch || createMatches(labelPositions) }; } } From 9225f892949326ab608321c25f440eb32dda8099 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Aug 2020 14:00:31 +0200 Subject: [PATCH 540/736] clean up --- .../contrib/extensions/browser/extensionRecommendations.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts index 985a4811904..9485211ea7e 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts @@ -65,7 +65,7 @@ export abstract class ExtensionRecommendations extends Disposable { } } - protected promptImportantExtensionsInstallNotification(extensionIds: string[], message: string, searchValue: string, showRecommendationsLabel?: string): void { + protected promptImportantExtensionsInstallNotification(extensionIds: string[], message: string, searchValue: string): void { this.notificationService.prompt(Severity.Info, message, [{ label: localize('install', 'Install'), @@ -76,7 +76,7 @@ export abstract class ExtensionRecommendations extends Disposable { this.runAction(this.instantiationService.createInstance(InstallRecommendedExtensionsAction, InstallRecommendedExtensionsAction.ID, InstallRecommendedExtensionsAction.LABEL, extensionIds, searchValue, 'install-recommendations')); } }, { - label: showRecommendationsLabel || localize('show recommendations', "Show Recommendations"), + label: localize('show recommendations', "Show Recommendations"), run: async () => { for (const extensionId of extensionIds) { this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'show', extensionId }); From c5fe5d7c1a1bb69ef83ac9064bea3210642d0cc4 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Aug 2020 15:05:16 +0200 Subject: [PATCH 541/736] open editors and pin --- .../contrib/extensions/browser/extensionRecommendations.ts | 4 +--- .../workbench/contrib/extensions/browser/extensionsActions.ts | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts index 9485211ea7e..9493fd7a182 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts @@ -80,11 +80,9 @@ export abstract class ExtensionRecommendations extends Disposable { run: async () => { for (const extensionId of extensionIds) { this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'show', extensionId }); + this.runAction(this.instantiationService.createInstance(OpenExtensionEditorAction, extensionId)); } this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); - if (extensionIds.length === 1) { - this.runAction(this.instantiationService.createInstance(OpenExtensionEditorAction, extensionIds[0])); - } } }, { label: choiceNever, diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 8639396bc57..8213b49d616 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -1953,7 +1953,7 @@ export class OpenExtensionEditorAction extends Action { const pager = await this.extensionWorkbenchService.queryGallery({ names: [this.extensionId], source: 'install-recommendation', pageSize: 1 }, CancellationToken.None); if (pager && pager.firstPage && pager.firstPage.length) { const extension = pager.firstPage[0]; - return this.extensionWorkbenchService.open(extension); + return this.extensionWorkbenchService.open(extension, { pinned: true }); } } } From bb2e67f6d6b9268442f3b6d38b6d20e43b9518a4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 26 Aug 2020 15:45:24 +0200 Subject: [PATCH 542/736] load tas-client async (fix #105343) (#105400) * load tas-client async (fix #105343) * use import type --- .../services/experiment/electron-browser/experimentService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/experiment/electron-browser/experimentService.ts b/src/vs/workbench/services/experiment/electron-browser/experimentService.ts index 86e4db99d35..b9af68bd937 100644 --- a/src/vs/workbench/services/experiment/electron-browser/experimentService.ts +++ b/src/vs/workbench/services/experiment/electron-browser/experimentService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as platform from 'vs/base/common/platform'; -import { IKeyValueStorage, IExperimentationTelemetry, IExperimentationFilterProvider, ExperimentationService as TASClient } from 'tas-client'; +import type { IKeyValueStorage, IExperimentationTelemetry, IExperimentationFilterProvider, ExperimentationService as TASClient } from 'tas-client'; import { MementoObject, Memento } from 'vs/workbench/common/memento'; import { IProductService } from 'vs/platform/product/common/productService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -206,7 +206,7 @@ export class ExperimentService implements ITASExperimentService { const telemetry = new ExperimentServiceTelemetry(this.telemetryService); const tasConfig = this.productService.tasConfig!; - const tasClient = new TASClient({ + const tasClient = new (await import('tas-client')).ExperimentationService({ filterProviders: [filterProvider], telemetry: telemetry, storageKey: storageKey, From 995302fd693123a3d59861dd4ab6630222f39c15 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 26 Aug 2020 15:45:58 +0200 Subject: [PATCH 543/736] Load 'node-pty' async (fix #105345) (#105401) * Load 'node-pty' async (fix #105345) * Use import type to prevent accidentally code import Co-authored-by: Daniel Imms --- src/vs/workbench/contrib/terminal/node/terminalProcess.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts b/src/vs/workbench/contrib/terminal/node/terminalProcess.ts index a007d9aa027..f3a9e3da81c 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts +++ b/src/vs/workbench/contrib/terminal/node/terminalProcess.ts @@ -5,7 +5,7 @@ import * as path from 'vs/base/common/path'; import * as platform from 'vs/base/common/platform'; -import * as pty from 'node-pty'; +import type * as pty from 'node-pty'; import * as fs from 'fs'; import { Event, Emitter } from 'vs/base/common/event'; import { getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/terminal'; @@ -90,7 +90,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess } try { - this.setupPtyProcess(this._shellLaunchConfig, this._ptyOptions); + await this.setupPtyProcess(this._shellLaunchConfig, this._ptyOptions); return undefined; } catch (err) { this._logService.trace('IPty#spawn native exception', err); @@ -136,10 +136,10 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess return undefined; } - private setupPtyProcess(shellLaunchConfig: IShellLaunchConfig, options: pty.IPtyForkOptions): void { + private async setupPtyProcess(shellLaunchConfig: IShellLaunchConfig, options: pty.IPtyForkOptions): Promise { const args = shellLaunchConfig.args || []; this._logService.trace('IPty#spawn', shellLaunchConfig.executable, args, options); - const ptyProcess = pty.spawn(shellLaunchConfig.executable!, args, options); + const ptyProcess = (await import('node-pty')).spawn(shellLaunchConfig.executable!, args, options); this._ptyProcess = ptyProcess; this._processStartupComplete = new Promise(c => { this.onProcessReady(() => c()); From b2b71a8296eaafe3b2ea7db4e44ea601e3b6cd05 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 26 Aug 2020 06:50:09 -0700 Subject: [PATCH 544/736] Sanity check lines[y] to fix exception Fixes #105333 --- .../contrib/terminal/browser/links/terminalLinkHelpers.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers.ts index 9297f97fd2a..e39db0a24d6 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers.ts @@ -54,6 +54,12 @@ export function convertLinkRangeToBuffer(lines: IBufferLine[], bufferWidth: numb const startLineOffset = (y === startWrappedLineCount - 1 ? startOffset : 0); let lineOffset = 0; const line = lines[y]; + // Sanity check for line, apparently this can happen but it's not clear under what + // circumstances this happens. Continue on, skipping the remainder of start offset if this + // happens to minimize impact. + if (!line) { + break; + } for (let x = start; x < Math.min(bufferWidth, lineLength + lineOffset + startLineOffset); x++) { const cell = line.getCell(x)!; const width = cell.getWidth(); From ec5025333173044c34dd2228b0560d0ab1b84d8d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 26 Aug 2020 16:06:05 +0200 Subject: [PATCH 545/736] fuzzy scorer - fix another corner case for prefix matches --- src/vs/base/common/fuzzyScorer.ts | 11 +++++++---- src/vs/base/test/common/fuzzyScorer.test.ts | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/vs/base/common/fuzzyScorer.ts b/src/vs/base/common/fuzzyScorer.ts index dfb734d8e61..12c3e4ad739 100644 --- a/src/vs/base/common/fuzzyScorer.ts +++ b/src/vs/base/common/fuzzyScorer.ts @@ -609,10 +609,13 @@ export function compareItemsByFuzzyScore(itemA: T, itemB: T, query: IPrepared return scoreA > scoreB ? -1 : 1; } - // prefer more compact matches over longer in label - const comparedByMatchLength = compareByMatchLength(itemScoreA.labelMatch, itemScoreB.labelMatch); - if (comparedByMatchLength !== 0) { - return comparedByMatchLength; + // prefer more compact matches over longer in label (unless this is a prefix match where + // longer prefix matches are actually preferred) + if (scoreA < LABEL_PREFIX_SCORE_THRESHOLD && scoreB < LABEL_PREFIX_SCORE_THRESHOLD) { + const comparedByMatchLength = compareByMatchLength(itemScoreA.labelMatch, itemScoreB.labelMatch); + if (comparedByMatchLength !== 0) { + return comparedByMatchLength; + } } // prefer shorter labels over longer labels diff --git a/src/vs/base/test/common/fuzzyScorer.test.ts b/src/vs/base/test/common/fuzzyScorer.test.ts index 1a7eca591ff..31f211e7ce7 100644 --- a/src/vs/base/test/common/fuzzyScorer.test.ts +++ b/src/vs/base/test/common/fuzzyScorer.test.ts @@ -1029,7 +1029,7 @@ suite('Fuzzy Scorer', () => { const resourceA = URI.file('src/vs/workbench/services/host/browser/browserHostService.ts'); const resourceB = URI.file('src/vs/workbench/browser/workbench.ts'); - for (const query of ['workbench.ts browser', 'browser workbench.ts']) { + for (const query of ['workbench.ts browser', 'browser workbench.ts', 'browser workbench', 'workbench browser']) { let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); From 353503a9183a14a917c10c6228d0254bbd6b8687 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 26 Aug 2020 16:33:07 +0200 Subject: [PATCH 546/736] Execute the editor's paste handler when pasting --- .../browser/controller/textAreaInput.ts | 2 +- src/vs/editor/contrib/clipboard/clipboard.ts | 26 +++++++++++++------ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/vs/editor/browser/controller/textAreaInput.ts b/src/vs/editor/browser/controller/textAreaInput.ts index 238e47cd542..7b2868aa701 100644 --- a/src/vs/editor/browser/controller/textAreaInput.ts +++ b/src/vs/editor/browser/controller/textAreaInput.ts @@ -72,7 +72,7 @@ interface InMemoryClipboardMetadata { * Every time we read from the cipboard, if the text matches our last written text, * we can fetch the previous metadata. */ -class InMemoryClipboardMetadataManager { +export class InMemoryClipboardMetadataManager { public static readonly INSTANCE = new InMemoryClipboardMetadataManager(); private _lastState: InMemoryClipboardMetadata | null; diff --git a/src/vs/editor/contrib/clipboard/clipboard.ts b/src/vs/editor/contrib/clipboard/clipboard.ts index c8e82074897..742792579c2 100644 --- a/src/vs/editor/contrib/clipboard/clipboard.ts +++ b/src/vs/editor/contrib/clipboard/clipboard.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import * as browser from 'vs/base/browser/browser'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import * as platform from 'vs/base/common/platform'; -import { CopyOptions } from 'vs/editor/browser/controller/textAreaInput'; +import { CopyOptions, InMemoryClipboardMetadataManager } from 'vs/editor/browser/controller/textAreaInput'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, registerEditorAction, Command, MultiCommand } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; @@ -17,7 +17,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Handler } from 'vs/editor/common/editorCommon'; const CLIPBOARD_CONTEXT_MENU_GROUP = '9_cutcopypaste'; @@ -187,12 +187,22 @@ function registerExecCommandImpl(target: MultiCommand | undefined, browserComman if (!result && platform.isWeb && browserCommand === 'paste') { (async () => { const clipboardText = await clipboardService.readText(); - - const selection = focusedEditor.getSelection(); - if (selection) { - const edits = [EditOperation.insert(selection.getPosition(), clipboardText)]; - - focusedEditor.executeEdits('clipboard', edits); + if (clipboardText !== '') { + const metadata = InMemoryClipboardMetadataManager.INSTANCE.get(clipboardText); + let pasteOnNewLine = false; + let multicursorText: string[] | null = null; + let mode: string | null = null; + if (metadata) { + pasteOnNewLine = (focusedEditor.getOption(EditorOption.emptySelectionClipboard) && !!metadata.isFromEmptySelection); + multicursorText = (typeof metadata.multicursorText !== 'undefined' ? metadata.multicursorText : null); + mode = metadata.mode; + } + focusedEditor.trigger('keyboard', Handler.Paste, { + text: clipboardText, + pasteOnNewLine, + multicursorText, + mode + }); } })(); return true; From 196500f3647d9e26b71054652ae79e7886803117 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 26 Aug 2020 15:57:22 +0200 Subject: [PATCH 547/736] notebook cell edit --- .../contrib/bulkEdit/browser/bulkCellEdits.ts | 57 +++++++++++++++++++ .../bulkEdit/browser/bulkEditService.ts | 44 ++++++++------ .../notebookEditorModelResolverService.ts | 15 +++-- 3 files changed, 94 insertions(+), 22 deletions(-) create mode 100644 src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts new file mode 100644 index 00000000000..6697646978b --- /dev/null +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { groupBy } from 'vs/base/common/arrays'; +import { compare } from 'vs/base/common/strings'; +import { URI } from 'vs/base/common/uri'; +import { ResourceEdit } from 'vs/editor/browser/services/bulkEditService'; +import { WorkspaceEditMetadata } from 'vs/editor/common/modes'; +import { IProgress } from 'vs/platform/progress/common/progress'; +import { ICellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; + +export class ResourceNotebookCellEdit extends ResourceEdit { + + constructor( + readonly resource: URI, + readonly cellEdit: ICellEditOperation, + readonly versionId?: number, + readonly metadata?: WorkspaceEditMetadata + ) { + super(metadata); + } +} + +export class BulkCellEdits { + + constructor( + private readonly _progress: IProgress, + private readonly _edits: ResourceNotebookCellEdit[], + @INotebookEditorModelResolverService private readonly _notebookModelService: INotebookEditorModelResolverService, + ) { } + + async apply(): Promise { + + const editsByNotebook = groupBy(this._edits, (a, b) => compare(a.resource.toString(), b.resource.toString())); + + for (let group of editsByNotebook) { + const [first] = group; + const ref = await this._notebookModelService.resolve(first.resource); + + // check state + if (typeof first.versionId === 'number' && ref.object.notebook.versionId !== first.versionId) { + ref.dispose(); + throw new Error(`Notebook '${first.resource}' has changed in the meantime`); + } + + // apply edits + const cellEdits = group.map(edit => edit.cellEdit); + ref.object.notebook.applyEdit(ref.object.notebook.versionId, cellEdits, true); + ref.dispose(); + + this._progress.report(undefined); + } + } +} diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts index 5900948f8b4..b1207b2323c 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts @@ -15,26 +15,19 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { BulkTextEdits } from 'vs/workbench/contrib/bulkEdit/browser/bulkTextEdits'; import { BulkFileEdits } from 'vs/workbench/contrib/bulkEdit/browser/bulkFileEdits'; +import { BulkCellEdits, ResourceNotebookCellEdit } from 'vs/workbench/contrib/bulkEdit/browser/bulkCellEdits'; class BulkEdit { - private readonly _label: string | undefined; - private readonly _edits: ResourceEdit[] = []; - private readonly _editor: ICodeEditor | undefined; - private readonly _progress: IProgress; - constructor( - label: string | undefined, - editor: ICodeEditor | undefined, - progress: IProgress | undefined, - edits: ResourceEdit[], + private readonly _label: string | undefined, + private readonly _editor: ICodeEditor | undefined, + private readonly _progress: IProgress, + private readonly _edits: ResourceEdit[], @IInstantiationService private readonly _instaService: IInstantiationService, @ILogService private readonly _logService: ILogService, ) { - this._label = label; - this._editor = editor; - this._progress = progress || Progress.None; - this._edits = edits; + } ariaMessage(): string { @@ -75,6 +68,8 @@ class BulkEdit { await this._performFileEdits(group, progress); } else if (group[0] instanceof ResourceTextEdit) { await this._performTextEdits(group, progress); + } else if (group[0] instanceof ResourceNotebookCellEdit) { + await this._performCellEdits(group, progress); } else { console.log('UNKNOWN EDIT'); } @@ -93,6 +88,12 @@ class BulkEdit { const model = this._instaService.createInstance(BulkTextEdits, this._label || localize('workspaceEdit', "Workspace Edit"), this._editor, progress, edits); await model.apply(); } + + private async _performCellEdits(edits: ResourceNotebookCellEdit[], progress: IProgress): Promise { + this._logService.debug('_performCellEdits', JSON.stringify(edits)); + const model = this._instaService.createInstance(BulkCellEdits, progress, edits); + await model.apply(); + } } export class BulkEditService implements IBulkEditService { @@ -130,7 +131,6 @@ export class BulkEditService implements IBulkEditService { edits = await this._previewHandler(edits, options); } - // const { edits } = edit; let codeEditor = options?.editor; // try to find code editor if (!codeEditor) { @@ -144,15 +144,23 @@ export class BulkEditService implements IBulkEditService { // If the code editor is readonly still allow bulk edits to be applied #68549 codeEditor = undefined; } - const bulkEdit = this._instaService.createInstance(BulkEdit, options?.quotableLabel || options?.label, codeEditor, options?.progress, edits); - return bulkEdit.perform().then(() => { + + const bulkEdit = this._instaService.createInstance( + BulkEdit, + options?.quotableLabel || options?.label, + codeEditor, options?.progress ?? Progress.None, + edits + ); + + try { + await bulkEdit.perform(); return { ariaSummary: bulkEdit.ariaMessage() }; - }).catch(err => { + } catch (err) { // console.log('apply FAILED'); // console.log(err); this._logService.error(err); throw err; - }); + } } } diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts index cedfaeef088..139c02ae5b4 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts @@ -15,7 +15,7 @@ export const INotebookEditorModelResolverService = createDecorator>; + resolve(resource: URI, viewType?: string, editorId?: string): Promise>; } @@ -30,9 +30,16 @@ export class NotebookModelReferenceCollection extends ReferenceCollection { - const [viewType, editorId] = args as [string, string | undefined]; - const resource = URI.parse(key); + + let [viewType, editorId] = args as [string | undefined, string | undefined]; + if (!viewType) { + viewType = this._notebookService.getContributedNotebookProviders(resource)[0]?.id; + } + if (!viewType) { + throw new Error('Missing viewType'); + } + const model = this._instantiationService.createInstance(NotebookEditorModel, resource, viewType); const promise = model.load({ editorId }); return promise; @@ -60,7 +67,7 @@ export class NotebookModelResolverService implements INotebookEditorModelResolve this._data = instantiationService.createInstance(NotebookModelReferenceCollection); } - async resolve(resource: URI, viewType: string, editorId?: string | undefined): Promise> { + async resolve(resource: URI, viewType?: string, editorId?: string | undefined): Promise> { const reference = this._data.acquire(resource.toString(), viewType, editorId); const model = await reference.object; return { From f5b42dbccfc7e08105ac68db00df35574c7bef2f Mon Sep 17 00:00:00 2001 From: Max Reynolds Date: Wed, 26 Aug 2020 15:51:54 +0100 Subject: [PATCH 548/736] Update error messages --- .../userDataSync/common/userDataSyncStoreService.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts index 5ace07e390a..0d9489ed8e9 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts @@ -394,7 +394,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync this._onTokenSucceed.fire(); if (context.res.statusCode === 409) { - throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of Conflict (409). There is new data exists for this resource. Make the request again with latest data.`, UserDataSyncErrorCode.Conflict, operationId); + throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of Conflict (409). There is new data for this resource. Make the request again with latest data.`, UserDataSyncErrorCode.Conflict, operationId); } if (context.res.statusCode === 410) { @@ -402,7 +402,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync } if (context.res.statusCode === 412) { - throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of Precondition Failed (412). There is new data exists for this resource. Make the request again with latest data.`, UserDataSyncErrorCode.PreconditionFailed, operationId); + throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of Precondition Failed (412). There is new data for this resource. Make the request again with latest data.`, UserDataSyncErrorCode.PreconditionFailed, operationId); } if (context.res.statusCode === 413) { @@ -482,7 +482,7 @@ export class RequestsSession { if (this.requests.length >= this.limit) { this.logService.info('Too many requests', ...this.requests); - throw new UserDataSyncStoreError(`Too many requests. Allowed only ${this.limit} requests in ${this.interval / (1000 * 60)} minutes.`, UserDataSyncErrorCode.LocalTooManyRequests, undefined); + throw new UserDataSyncStoreError(`Too many requests. Only ${this.limit} requests allowed in ${this.interval / (1000 * 60)} minutes.`, UserDataSyncErrorCode.LocalTooManyRequests, undefined); } this.startTime = this.startTime || new Date(); From 9345bb7ab6d36d5b43d50b661535acfdf15e2b26 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 26 Aug 2020 16:53:49 +0200 Subject: [PATCH 549/736] Separate the Paste implementation from the Cut & Copy one --- src/vs/editor/contrib/clipboard/clipboard.ts | 98 +++++++++++--------- 1 file changed, 55 insertions(+), 43 deletions(-) diff --git a/src/vs/editor/contrib/clipboard/clipboard.ts b/src/vs/editor/contrib/clipboard/clipboard.ts index 742792579c2..cbdeb7424d3 100644 --- a/src/vs/editor/contrib/clipboard/clipboard.ts +++ b/src/vs/editor/contrib/clipboard/clipboard.ts @@ -25,7 +25,6 @@ const supportsCut = (platform.isNative || document.queryCommandSupported('cut')) const supportsCopy = (platform.isNative || document.queryCommandSupported('copy')); // IE and Edge have trouble with setting html content in clipboard const supportsCopyWithSyntaxHighlighting = (supportsCopy && !browser.isEdge); -const supportsPaste = true; function registerCommand(command: T): T { command.register(); @@ -93,7 +92,7 @@ export const CopyAction = supportsCopy ? registerCommand(new MultiCommand({ }] })) : undefined; -export const PasteAction = supportsPaste ? registerCommand(new MultiCommand({ +export const PasteAction = registerCommand(new MultiCommand({ id: 'editor.action.clipboardPasteAction', precondition: undefined, kbOpts: ( @@ -123,7 +122,7 @@ export const PasteAction = supportsPaste ? registerCommand(new MultiCommand({ title: nls.localize('actions.clipboard.pasteLabel', "Paste"), order: 1 }] -})) : undefined; +})); class ExecCommandCopyWithSyntaxHighlightingAction extends EditorAction { @@ -159,54 +158,23 @@ class ExecCommandCopyWithSyntaxHighlightingAction extends EditorAction { } } -function registerExecCommandImpl(target: MultiCommand | undefined, browserCommand: 'cut' | 'copy' | 'paste'): void { +function registerExecCommandImpl(target: MultiCommand | undefined, browserCommand: 'cut' | 'copy'): void { if (!target) { return; } // 1. handle case when focus is in editor. target.addImplementation(10000, (accessor: ServicesAccessor, args: any) => { - const codeEditorService = accessor.get(ICodeEditorService); - const clipboardService = accessor.get(IClipboardService); - // Only if editor text focus (i.e. not if editor has widget focus). - const focusedEditor = codeEditorService.getFocusedCodeEditor(); + const focusedEditor = accessor.get(ICodeEditorService).getFocusedCodeEditor(); if (focusedEditor && focusedEditor.hasTextFocus()) { - if (browserCommand === 'cut' || browserCommand === 'copy') { - // Do not execute if there is no selection and empty selection clipboard is off - const emptySelectionClipboard = focusedEditor.getOption(EditorOption.emptySelectionClipboard); - const selection = focusedEditor.getSelection(); - if (selection && selection.isEmpty() && !emptySelectionClipboard) { - return true; - } - } - const result = document.execCommand(browserCommand); - // Web: certain browsers do not allow document.execCommand('paste') - // and as such we implement a workaround via the clipboard service - // Refs: https://github.com/microsoft/vscode/issues/82604 - if (!result && platform.isWeb && browserCommand === 'paste') { - (async () => { - const clipboardText = await clipboardService.readText(); - if (clipboardText !== '') { - const metadata = InMemoryClipboardMetadataManager.INSTANCE.get(clipboardText); - let pasteOnNewLine = false; - let multicursorText: string[] | null = null; - let mode: string | null = null; - if (metadata) { - pasteOnNewLine = (focusedEditor.getOption(EditorOption.emptySelectionClipboard) && !!metadata.isFromEmptySelection); - multicursorText = (typeof metadata.multicursorText !== 'undefined' ? metadata.multicursorText : null); - mode = metadata.mode; - } - focusedEditor.trigger('keyboard', Handler.Paste, { - text: clipboardText, - pasteOnNewLine, - multicursorText, - mode - }); - } - })(); + // Do not execute if there is no selection and empty selection clipboard is off + const emptySelectionClipboard = focusedEditor.getOption(EditorOption.emptySelectionClipboard); + const selection = focusedEditor.getSelection(); + if (selection && selection.isEmpty() && !emptySelectionClipboard) { return true; } + document.execCommand(browserCommand); return true; } return false; @@ -214,7 +182,6 @@ function registerExecCommandImpl(target: MultiCommand | undefined, browserComman // 2. (default) handle case when focus is somewhere else. target.addImplementation(0, (accessor: ServicesAccessor, args: any) => { - // Only if editor text focus (i.e. not if editor has widget focus). document.execCommand(browserCommand); return true; }); @@ -222,7 +189,52 @@ function registerExecCommandImpl(target: MultiCommand | undefined, browserComman registerExecCommandImpl(CutAction, 'cut'); registerExecCommandImpl(CopyAction, 'copy'); -registerExecCommandImpl(PasteAction, 'paste'); + +// 1. Paste: handle case when focus is in editor. +PasteAction.addImplementation(10000, (accessor: ServicesAccessor, args: any) => { + const codeEditorService = accessor.get(ICodeEditorService); + const clipboardService = accessor.get(IClipboardService); + + // Only if editor text focus (i.e. not if editor has widget focus). + const focusedEditor = codeEditorService.getFocusedCodeEditor(); + if (focusedEditor && focusedEditor.hasTextFocus()) { + const result = document.execCommand('paste'); + // Web: certain browsers do not allow document.execCommand('paste') + // and as such we implement a workaround via the clipboard service + // Refs: https://github.com/microsoft/vscode/issues/82604 + if (!result && platform.isWeb) { + (async () => { + const clipboardText = await clipboardService.readText(); + if (clipboardText !== '') { + const metadata = InMemoryClipboardMetadataManager.INSTANCE.get(clipboardText); + let pasteOnNewLine = false; + let multicursorText: string[] | null = null; + let mode: string | null = null; + if (metadata) { + pasteOnNewLine = (focusedEditor.getOption(EditorOption.emptySelectionClipboard) && !!metadata.isFromEmptySelection); + multicursorText = (typeof metadata.multicursorText !== 'undefined' ? metadata.multicursorText : null); + mode = metadata.mode; + } + focusedEditor.trigger('keyboard', Handler.Paste, { + text: clipboardText, + pasteOnNewLine, + multicursorText, + mode + }); + } + })(); + return true; + } + return true; + } + return false; +}); + +// 2. Paste: (default) handle case when focus is somewhere else. +PasteAction.addImplementation(0, (accessor: ServicesAccessor, args: any) => { + document.execCommand('paste'); + return true; +}); if (supportsCopyWithSyntaxHighlighting) { registerEditorAction(ExecCommandCopyWithSyntaxHighlightingAction); From 3613fc981b528919afba240f7a98e155d52d65dd Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 25 Aug 2020 21:38:50 -0700 Subject: [PATCH 550/736] Don't take over 'enter' in split json editor Fix #105324 --- .../contrib/preferences/browser/preferences.contribution.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index 5624c5cd5e9..42a9848038a 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -40,6 +40,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { DefaultPreferencesEditorInput, KeybindingsEditorInput, PreferencesEditorInput, SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { AbstractSideBySideEditorInputFactory } from 'vs/workbench/browser/parts/editor/editor.contribution'; +import { WorkbenchListFocusContextKey } from 'vs/platform/list/browser/listService'; const SETTINGS_EDITOR_COMMAND_SEARCH = 'settings.action.search'; @@ -746,7 +747,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon constructor() { super({ id: SETTINGS_EDITOR_COMMAND_FOCUS_CONTROL, - precondition: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_TOC_ROW_FOCUS.negate()), + precondition: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_TOC_ROW_FOCUS.negate(), WorkbenchListFocusContextKey), keybinding: { primary: KeyCode.Enter, weight: KeybindingWeight.WorkbenchContrib, From 6be16f9a1641e4c60d7b6aa9b593f5379c8ce58c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Aug 2020 17:04:20 +0200 Subject: [PATCH 551/736] Fix #104496 --- .../common/userDataAutoSyncService.ts | 20 +++++------ .../userDataSync/common/userDataSync.ts | 4 +-- .../userDataSync/common/userDataSyncIpc.ts | 2 +- .../common/userDataSyncService.ts | 8 +++-- .../userDataAutoSyncService.ts | 2 +- .../common/userDataAutoSyncService.test.ts | 34 ++++++++++++++++--- .../browser/userDataAutoSyncService.ts | 2 +- .../userDataSync/browser/userDataSync.ts | 2 +- .../userDataAutoSyncService.ts | 6 ++-- .../sandbox.simpleservices.ts | 2 +- 10 files changed, 55 insertions(+), 27 deletions(-) diff --git a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts index 097be01f51d..9574d24e419 100644 --- a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts @@ -131,8 +131,8 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i this._register(userDataSyncAccountService.onDidChangeAccount(() => this.updateAutoSync())); this._register(userDataSyncStoreService.onDidChangeDonotMakeRequestsUntil(() => this.updateAutoSync())); - this._register(Event.debounce(userDataSyncService.onDidChangeLocal, (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerSync(sources, false))); - this._register(Event.filter(this.userDataSyncResourceEnablementService.onDidChangeResourceEnablement, ([, enabled]) => enabled)(() => this.triggerSync(['resourceEnablement'], false))); + this._register(Event.debounce(userDataSyncService.onDidChangeLocal, (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerSync(sources, false, false))); + this._register(Event.filter(this.userDataSyncResourceEnablementService.onDidChangeResourceEnablement, ([, enabled]) => enabled)(() => this.triggerSync(['resourceEnablement'], false, false))); } } @@ -320,7 +320,7 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i } private sources: string[] = []; - async triggerSync(sources: string[], skipIfSyncedRecently: boolean): Promise { + async triggerSync(sources: string[], skipIfSyncedRecently: boolean, disableCache: boolean): Promise { if (this.autoSync.value === undefined) { return this.syncTriggerDelayer.cancel(); } @@ -337,7 +337,7 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i this.telemetryService.publicLog2<{ sources: string[] }, AutoSyncClassification>('sync/triggered', { sources: this.sources }); this.sources = []; if (this.autoSync.value) { - await this.autoSync.value.sync('Activity'); + await this.autoSync.value.sync('Activity', disableCache); } }, this.successiveFailures ? this.getSyncTriggerDelayTime() * 1 * Math.min(Math.pow(2, this.successiveFailures), 60) /* Delay exponentially until max 1 minute */ @@ -393,14 +393,14 @@ class AutoSync extends Disposable { this.logService.info('Auto Sync: Stopped'); })); this.logService.info('Auto Sync: Started'); - this.sync(AutoSync.INTERVAL_SYNCING); + this.sync(AutoSync.INTERVAL_SYNCING, false); } private waitUntilNextIntervalAndSync(): void { - this.intervalHandler.value = disposableTimeout(() => this.sync(AutoSync.INTERVAL_SYNCING), this.interval); + this.intervalHandler.value = disposableTimeout(() => this.sync(AutoSync.INTERVAL_SYNCING, false), this.interval); } - sync(reason: string): Promise { + sync(reason: string, disableCache: boolean): Promise { const syncPromise = createCancelablePromise(async token => { if (this.syncPromise) { try { @@ -414,7 +414,7 @@ class AutoSync extends Disposable { } } } - return this.doSync(reason, token); + return this.doSync(reason, disableCache, token); }); this.syncPromise = syncPromise; this.syncPromise.finally(() => this.syncPromise = undefined); @@ -435,12 +435,12 @@ class AutoSync extends Disposable { !isEqual(current.stableUrl, previous.stableUrl)); } - private async doSync(reason: string, token: CancellationToken): Promise { + private async doSync(reason: string, disableCache: boolean, token: CancellationToken): Promise { this.logService.info(`Auto Sync: Triggered by ${reason}`); this._onDidStartSync.fire(); let error: Error | undefined; try { - this.syncTask = await this.userDataSyncService.createSyncTask(); + this.syncTask = await this.userDataSyncService.createSyncTask(disableCache); if (token.isCancellationRequested) { return; } diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 123519b253d..2381e7e7345 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -437,7 +437,7 @@ export interface IUserDataSyncService { readonly onDidResetRemote: Event; readonly onDidResetLocal: Event; - createSyncTask(): Promise; + createSyncTask(disableCache?: boolean): Promise; createManualSyncTask(): Promise; replace(uri: URI): Promise; @@ -465,7 +465,7 @@ export interface IUserDataAutoSyncService { canToggleEnablement(): boolean; turnOn(): Promise; turnOff(everywhere: boolean): Promise; - triggerSync(sources: string[], hasToLimitSync: boolean): Promise; + triggerSync(sources: string[], hasToLimitSync: boolean, disableCache: boolean): Promise; } export const IUserDataSyncUtilService = createDecorator('IUserDataSyncUtilService'); diff --git a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts index 8d6296e8051..08a8243bb42 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts @@ -128,7 +128,7 @@ export class UserDataAutoSyncChannel implements IServerChannel { call(context: any, command: string, args?: any): Promise { switch (command) { - case 'triggerSync': return this.service.triggerSync(args[0], args[1]); + case 'triggerSync': return this.service.triggerSync(args[0], args[1], args[2]); case 'turnOn': return this.service.turnOn(); case 'turnOff': return this.service.turnOff(args[0]); } diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index f9c446c2124..54d42878bf5 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -106,13 +106,17 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeLocal, () => s.resource))); } - async createSyncTask(): Promise { + async createSyncTask(disableCache?: boolean): Promise { await this.checkEnablement(); const executionId = generateUuid(); let manifest: IUserDataManifest | null; try { - manifest = await this.userDataSyncStoreService.manifest(createSyncHeaders(executionId)); + const syncHeaders = createSyncHeaders(executionId); + if (disableCache) { + syncHeaders['Cache-Control'] = 'no-cache'; + } + manifest = await this.userDataSyncStoreService.manifest(syncHeaders); } catch (error) { error = UserDataSyncError.toUserDataSyncError(error); this.telemetryService.publicLog2<{ code: string, service: string, resource?: string, executionId?: string }, SyncErrorClassification>('sync/error', { code: error.code, resource: error.resource, executionId, service: this.userDataSyncStoreManagementService.userDataSyncStore!.url.toString() }); diff --git a/src/vs/platform/userDataSync/electron-browser/userDataAutoSyncService.ts b/src/vs/platform/userDataSync/electron-browser/userDataAutoSyncService.ts index 81483659e9b..44ed7f9b7ea 100644 --- a/src/vs/platform/userDataSync/electron-browser/userDataAutoSyncService.ts +++ b/src/vs/platform/userDataSync/electron-browser/userDataAutoSyncService.ts @@ -33,7 +33,7 @@ export class UserDataAutoSyncService extends BaseUserDataAutoSyncService { this._register(Event.debounce(Event.any( Event.map(electronService.onWindowFocus, () => 'windowFocus'), Event.map(electronService.onWindowOpen, () => 'windowOpen'), - ), (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerSync(sources, true))); + ), (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerSync(sources, true, false))); } } diff --git a/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts b/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts index 310c69dd6cf..49df9ea7e0f 100644 --- a/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts +++ b/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts @@ -20,7 +20,7 @@ class TestUserDataAutoSyncService extends UserDataAutoSyncService { protected getSyncTriggerDelayTime(): number { return 50; } sync(): Promise { - return this.triggerSync(['sync'], false); + return this.triggerSync(['sync'], false, false); } } @@ -43,7 +43,7 @@ suite('UserDataAutoSyncService', () => { const testObject: UserDataAutoSyncService = client.instantiationService.createInstance(TestUserDataAutoSyncService); // Trigger auto sync with settings change - await testObject.triggerSync([SyncResource.Settings], false); + await testObject.triggerSync([SyncResource.Settings], false, false); // Filter out machine requests const actual = target.requests.filter(request => !request.url.startsWith(`${target.url}/v1/resource/machines`)); @@ -66,7 +66,7 @@ suite('UserDataAutoSyncService', () => { // Trigger auto sync with settings change multiple times for (let counter = 0; counter < 2; counter++) { - await testObject.triggerSync([SyncResource.Settings], false); + await testObject.triggerSync([SyncResource.Settings], false, false); } // Filter out machine requests @@ -91,7 +91,7 @@ suite('UserDataAutoSyncService', () => { const testObject: UserDataAutoSyncService = client.instantiationService.createInstance(TestUserDataAutoSyncService); // Trigger auto sync with window focus once - await testObject.triggerSync(['windowFocus'], true); + await testObject.triggerSync(['windowFocus'], true, false); // Filter out machine requests const actual = target.requests.filter(request => !request.url.startsWith(`${target.url}/v1/resource/machines`)); @@ -114,7 +114,7 @@ suite('UserDataAutoSyncService', () => { // Trigger auto sync with window focus multiple times for (let counter = 0; counter < 2; counter++) { - await testObject.triggerSync(['windowFocus'], true); + await testObject.triggerSync(['windowFocus'], true, false); } // Filter out machine requests @@ -401,4 +401,28 @@ suite('UserDataAutoSyncService', () => { assert.deepEqual(target.requests, []); }); + test('test cache control header with no cache is sent when triggered with disable cache option', async () => { + const target = new UserDataSyncTestServer(5, 1); + + // Set up and sync from the test client + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject: TestUserDataAutoSyncService = testClient.instantiationService.createInstance(TestUserDataAutoSyncService); + + await testObject.triggerSync(['some reason'], true, true); + assert.equal(target.requestsWithAllHeaders[0].headers!['Cache-Control'], 'no-cache'); + }); + + test('test cache control header is not sent when triggered without disable cache option', async () => { + const target = new UserDataSyncTestServer(5, 1); + + // Set up and sync from the test client + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject: TestUserDataAutoSyncService = testClient.instantiationService.createInstance(TestUserDataAutoSyncService); + + await testObject.triggerSync(['some reason'], true, false); + assert.equal(target.requestsWithAllHeaders[0].headers!['Cache-Control'], undefined); + }); + }); diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService.ts index 9b326f949d5..2c3b7295c73 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService.ts @@ -36,7 +36,7 @@ export class UserDataAutoSyncService extends BaseUserDataAutoSyncService { this._register(Event.debounce(Event.any( Event.map(hostService.onDidChangeFocus, () => 'windowFocus'), instantiationService.createInstance(UserDataSyncTrigger).onDidTriggerSync, - ), (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerSync(sources, true))); + ), (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerSync(sources, true, false))); } } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 56ab09a0ba8..d8625ab699d 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -1031,7 +1031,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo }); } run(accessor: ServicesAccessor): Promise { - return that.userDataAutoSyncService.triggerSync([syncNowCommand.id], false); + return that.userDataAutoSyncService.triggerSync([syncNowCommand.id], false, true); } })); } diff --git a/src/vs/workbench/contrib/userDataSync/electron-browser/userDataAutoSyncService.ts b/src/vs/workbench/contrib/userDataSync/electron-browser/userDataAutoSyncService.ts index 35fde76ad97..20ebeff1c5d 100644 --- a/src/vs/workbench/contrib/userDataSync/electron-browser/userDataAutoSyncService.ts +++ b/src/vs/workbench/contrib/userDataSync/electron-browser/userDataAutoSyncService.ts @@ -29,11 +29,11 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i ) { super(storageService, environmentService, userDataSyncStoreManagementService); this.channel = sharedProcessService.getChannel('userDataAutoSync'); - this._register(instantiationService.createInstance(UserDataSyncTrigger).onDidTriggerSync(source => this.triggerSync([source], true))); + this._register(instantiationService.createInstance(UserDataSyncTrigger).onDidTriggerSync(source => this.triggerSync([source], true, false))); } - triggerSync(sources: string[], hasToLimitSync: boolean): Promise { - return this.channel.call('triggerSync', [sources, hasToLimitSync]); + triggerSync(sources: string[], hasToLimitSync: boolean, disableCache: boolean): Promise { + return this.channel.call('triggerSync', [sources, hasToLimitSync, disableCache]); } turnOn(): Promise { diff --git a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts index 8fff37e1f8d..61352e2ee83 100644 --- a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts +++ b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts @@ -718,7 +718,7 @@ class SimpleUserDataAutoSyncAccountService implements IUserDataAutoSyncService { canToggleEnablement(): boolean { return false; } async turnOn(): Promise { } async turnOff(everywhere: boolean): Promise { } - async triggerSync(sources: string[], hasToLimitSync: boolean): Promise { } + async triggerSync(sources: string[], hasToLimitSync: boolean, disableCache: boolean): Promise { } } registerSingleton(IUserDataAutoSyncService, SimpleUserDataAutoSyncAccountService); From 8871a289639fec7c24a4722357133fb33f64ca9b Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Mon, 24 Aug 2020 18:43:59 -0700 Subject: [PATCH 552/736] Make github-authentication a UI extension again --- extensions/github-authentication/package.json | 5 +++++ .../api/browser/mainThreadAuthentication.ts | 2 +- src/vs/workbench/api/common/extHost.protocol.ts | 2 +- .../api/common/extHostExtensionService.ts | 6 +++++- .../extensions/common/abstractExtensionService.ts | 10 +++++++--- .../extensions/common/extensionHostManager.ts | 14 ++++++++++---- .../services/extensions/common/extensions.ts | 7 ++++++- 7 files changed, 35 insertions(+), 11 deletions(-) diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index c4189e6b7eb..787b4d17497 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -11,6 +11,11 @@ "categories": [ "Other" ], + "extensionKind": [ + "ui", + "workspace", + "web" + ], "activationEvents": [ "*", "onAuthenticationRequest:github" diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts index 7a9e0fc6a64..e4fde65fb10 100644 --- a/src/vs/workbench/api/browser/mainThreadAuthentication.ts +++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts @@ -249,7 +249,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu } $ensureProvider(id: string): Promise { - return this.extensionService.activateByEvent(getAuthenticationProviderActivationEvent(id)); + return this.extensionService.activateByEvent(getAuthenticationProviderActivationEvent(id), true); } $sendDidChangeSessions(id: string, event: modes.AuthenticationSessionsChangeEvent): void { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index e09f81490e8..9f0c4e55065 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1074,7 +1074,7 @@ export type IResolveAuthorityResult = IResolveAuthorityErrorResult | IResolveAut export interface ExtHostExtensionServiceShape { $resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise; $startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise; - $activateByEvent(activationEvent: string): Promise; + $activateByEvent(activationEvent: string, eager?: boolean): Promise; $activate(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise; $setRemoteEnvironment(env: { [key: string]: string | null; }): Promise; $updateRemoteConnectionData(connectionData: IRemoteConnectionData): Promise; diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 6c5dcf97dad..0653b623ba0 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -686,7 +686,11 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme return this._startExtensionHost(); } - public $activateByEvent(activationEvent: string): Promise { + public $activateByEvent(activationEvent: string, eager: boolean = true): Promise { + if (eager) { + return this._activateByEvent(activationEvent, false); + } + return ( this._readyToRunExtensions.wait() .then(_ => this._activateByEvent(activationEvent, false)) diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index 446f74eff84..2bd2b6a3282 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -186,7 +186,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx this._startExtensionHosts(false, Array.from(this._allRequestedActivateEvents.keys())); } - public activateByEvent(activationEvent: string): Promise { + public activateByEvent(activationEvent: string, eager?: boolean): Promise { if (this._installedExtensionsReady.isOpen()) { // Extensions have been scanned and interpreted @@ -205,13 +205,17 @@ export abstract class AbstractExtensionService extends Disposable implements IEx // Record the fact that this activationEvent was requested (in case of a restart) this._allRequestedActivateEvents.add(activationEvent); + if (eager) { + return this._activateByEvent(activationEvent, eager); + } + return this._installedExtensionsReady.wait().then(() => this._activateByEvent(activationEvent)); } } - private _activateByEvent(activationEvent: string): Promise { + private _activateByEvent(activationEvent: string, eager?: boolean): Promise { const result = Promise.all( - this._extensionHostManagers.map(extHostManager => extHostManager.activateByEvent(activationEvent)) + this._extensionHostManagers.map(extHostManager => extHostManager.activateByEvent(activationEvent, eager)) ).then(() => { }); this._onWillActivateByEvent.fire({ event: activationEvent, diff --git a/src/vs/workbench/services/extensions/common/extensionHostManager.ts b/src/vs/workbench/services/extensions/common/extensionHostManager.ts index 484444e968d..e3f7f0c117a 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostManager.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostManager.ts @@ -48,6 +48,7 @@ export class ExtensionHostManager extends Disposable { */ private _proxy: Promise<{ value: ExtHostExtensionServiceShape; } | null> | null; private _resolveAuthorityAttempt: number; + private _hasStarted = false; constructor( extensionHost: IExtensionHost, @@ -65,6 +66,7 @@ export class ExtensionHostManager extends Disposable { this.onDidExit = this._extensionHost.onExit; this._proxy = this._extensionHost.start()!.then( (protocol) => { + this._hasStarted = true; return { value: this._createExtensionHostCustomers(protocol) }; }, (err) => { @@ -217,14 +219,18 @@ export class ExtensionHostManager extends Disposable { return proxy.$activate(extension, reason); } - public activateByEvent(activationEvent: string): Promise { + public activateByEvent(activationEvent: string, eager?: boolean): Promise { + if (eager && !this._hasStarted) { + return Promise.resolve(); + } + if (!this._cachedActivationEvents.has(activationEvent)) { - this._cachedActivationEvents.set(activationEvent, this._activateByEvent(activationEvent)); + this._cachedActivationEvents.set(activationEvent, this._activateByEvent(activationEvent, eager)); } return this._cachedActivationEvents.get(activationEvent)!; } - private async _activateByEvent(activationEvent: string): Promise { + private async _activateByEvent(activationEvent: string, eager?: boolean): Promise { if (!this._proxy) { return; } @@ -234,7 +240,7 @@ export class ExtensionHostManager extends Disposable { // i.e. the extension host could not be started return; } - return proxy.value.$activateByEvent(activationEvent); + return proxy.value.$activateByEvent(activationEvent, eager); } public async getInspectPort(tryEnableInspector: boolean): Promise { diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 652a2603156..f0980b0405c 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -177,8 +177,13 @@ export interface IExtensionService { /** * Send an activation event and activate interested extensions. + * + * Normally, this will queue the activation event if the extension hosts are not ready + * and send it to all of them. If the extension needs to be activated before this, + * the eager flag can be used to ignore extension hosts that aren't yet started. Do not + * use this flag unless necessary. */ - activateByEvent(activationEvent: string): Promise; + activateByEvent(activationEvent: string, eager?: boolean): Promise; /** * An promise that resolves when the installed extensions are registered after From a4850d52db4d9837a3a98d7cbfd3b6063cf41888 Mon Sep 17 00:00:00 2001 From: n-gist Date: Wed, 26 Aug 2020 22:15:09 +0600 Subject: [PATCH 553/736] Implementation TM_SELECTED_TEXT snippets variable for working with overtyped text --- src/vs/editor/common/model.ts | 6 ++++ src/vs/editor/common/model/editStack.ts | 18 +++++++++++ src/vs/editor/common/model/textModel.ts | 31 +++++++++++++++++-- .../editor/contrib/snippet/snippetSession.ts | 3 +- .../contrib/snippet/snippetVariables.ts | 20 +++++++++--- .../snippet/test/snippetVariables.test.ts | 10 +++--- 6 files changed, 76 insertions(+), 12 deletions(-) diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index d15e79eab7d..6eead87c480 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -1137,6 +1137,12 @@ export interface ITextModel { */ _applyRedo(changes: TextChange[], eol: EndOfLineSequence, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void; + /** + * Returns the text that has just been overtyped + * @internal + */ + getOvertypedText(overtypeIdx: number, undoSearchLimit: number, editSizeLimit: number): string | undefined; + /** * Undo edit operations until the first previous stop point created by `pushStackElement`. * The inverse edit operations will be pushed on the redo stack. diff --git a/src/vs/editor/common/model/editStack.ts b/src/vs/editor/common/model/editStack.ts index 5316fce9e95..a1d3b051d4e 100644 --- a/src/vs/editor/common/model/editStack.ts +++ b/src/vs/editor/common/model/editStack.ts @@ -198,6 +198,24 @@ export class SingleModelEditStackElement implements IResourceUndoRedoElement { } } + public getOvertypedTextAtEditingEnd(overtypeIdx: number, editingEnd: number): { text: string | undefined, continuousEditing: boolean, previousEditingEnd: number } { + let continuousEditing = false; + let overtypedText: string | undefined; + const data = (this._data instanceof SingleModelEditStackData ? this._data : SingleModelEditStackData.deserialize(this._data)); + const change = data.changes[overtypeIdx >= data.changes.length ? 0 : overtypeIdx]; + const inserted = change.oldLength === 0; + const deleted = change.newLength === 0; + if (inserted !== deleted) { + // If change is insert, and starts with new line, break the search + if (!(inserted && /^[\r\n]/.test(change.newText))) { + continuousEditing = editingEnd < 0 ? true : change.newEnd === editingEnd; + } + } else if (editingEnd < 0 || change.newEnd === editingEnd) { + overtypedText = change.oldText; + } + return { text: overtypedText, continuousEditing: continuousEditing, previousEditingEnd: change.oldEnd }; + } + public undo(): void { if (URI.isUri(this.model)) { // don't have a model diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index cb7b34b3451..58801ac093e 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -16,7 +16,7 @@ import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import * as model from 'vs/editor/common/model'; -import { EditStack } from 'vs/editor/common/model/editStack'; +import { EditStack, SingleModelEditStackElement } from 'vs/editor/common/model/editStack'; import { guessIndentation } from 'vs/editor/common/model/indentationGuesser'; import { IntervalNode, IntervalTree, getNodeIsInOverviewRuler, recomputeMaxEnd } from 'vs/editor/common/model/intervalTree'; import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder'; @@ -34,7 +34,7 @@ import { VSBufferReadableStream, VSBuffer } from 'vs/base/common/buffer'; import { TokensStore, MultilineTokens, countEOL, MultilineTokens2, TokensStore2 } from 'vs/editor/common/model/tokensStore'; import { Color } from 'vs/base/common/color'; import { EditorTheme } from 'vs/editor/common/view/viewContext'; -import { IUndoRedoService, ResourceEditStackSnapshot } from 'vs/platform/undoRedo/common/undoRedo'; +import { IUndoRedoService, ResourceEditStackSnapshot, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo'; import { TextChange } from 'vs/editor/common/model/textChange'; import { Constants } from 'vs/base/common/uint'; @@ -1491,6 +1491,33 @@ export class TextModel extends Disposable implements model.ITextModel { return (result.reverseEdits === null ? undefined : result.reverseEdits); } + public getOvertypedText(overtypeIdx: number, undoSearchLimit: number, editSizeLimit: number): string | undefined { + const elements = this._undoRedoService.getElements(this.uri).past; + if (elements.length === 0) { + return; + } + + // Cycle through undo elements to find one containing text that was overtyped + let editingEnd = -1; + let elementIndex = elements.length; + do { + const element = elements[--elementIndex]; + if (!(element.type === UndoRedoElementType.Resource && element instanceof SingleModelEditStackElement)) { + return; + } + const searchResult = element.getOvertypedTextAtEditingEnd(overtypeIdx, editingEnd); + if (searchResult.text) { + return searchResult.text; + } + if (!searchResult.continuousEditing || (editingEnd >= 0 && Math.abs(editingEnd - searchResult.previousEditingEnd) > editSizeLimit)) { + return; + } + editingEnd = searchResult.previousEditingEnd; + } while (elementIndex > 0 && --undoSearchLimit > 0); + + return; + } + public undo(): void { this._undoRedoService.undo(this.uri); } diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index 717b223cb4a..465b5401496 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -415,6 +415,7 @@ export class SnippetSession { .map((selection, idx) => ({ selection, idx })) .sort((a, b) => Range.compareRangesUsingStarts(a.selection, b.selection)); + let overtypedIdx = 0; // Makes overtyping snippets working with multiselections for (const { selection, idx } of indexedSelections) { // extend selection with the `overwriteBefore` and `overwriteAfter` and then @@ -449,7 +450,7 @@ export class SnippetSession { snippet.resolveVariables(new CompositeSnippetVariableResolver([ modelBasedVariableResolver, new ClipboardBasedVariableResolver(readClipboardText, idx, indexedSelections.length, editor.getOption(EditorOption.multiCursorPaste) === 'spread'), - new SelectionBasedVariableResolver(model, selection), + new SelectionBasedVariableResolver(model, selection, overtypedIdx++), new CommentBasedVariableResolver(model, selection), new TimeBasedVariableResolver, new WorkspaceBasedVariableResolver(workspaceService), diff --git a/src/vs/editor/contrib/snippet/snippetVariables.ts b/src/vs/editor/contrib/snippet/snippetVariables.ts index 3253898bdee..a788ca3b47f 100644 --- a/src/vs/editor/contrib/snippet/snippetVariables.ts +++ b/src/vs/editor/contrib/snippet/snippetVariables.ts @@ -71,7 +71,8 @@ export class SelectionBasedVariableResolver implements VariableResolver { constructor( private readonly _model: ITextModel, - private readonly _selection: Selection + private readonly _selection: Selection, + private readonly _overtypeIdx: number ) { // } @@ -82,14 +83,25 @@ export class SelectionBasedVariableResolver implements VariableResolver { if (name === 'SELECTION' || name === 'TM_SELECTED_TEXT') { let value = this._model.getValueInRange(this._selection) || undefined; - if (value && this._selection.startLineNumber !== this._selection.endLineNumber && variable.snippet) { + + // If there is no selected text, try to get overtyped text + let overtyped = false; + if (!value) { + const maxTypos = 10; // Allows the user to make 10 typos (delete+insert) when typing the snippet prefix + const maxEditSize = 50; // Limits the searching by edit size less than 50 symbols + value = this._model.getOvertypedText(this._overtypeIdx, 1 + 2 * maxTypos, maxEditSize); + if (value) { + overtyped = true; + } + } + + if (value && (overtyped || (this._selection.startLineNumber !== this._selection.endLineNumber)) && variable.snippet) { // Selection is a multiline string which we indentation we now // need to adjust. We compare the indentation of this variable // with the indentation at the editor position and add potential // extra indentation to the value - const line = this._model.getLineContent(this._selection.startLineNumber); - const lineLeadingWhitespace = getLeadingWhitespace(line, 0, this._selection.startColumn - 1); + const lineLeadingWhitespace = overtyped ? '' : getLeadingWhitespace(this._model.getLineContent(this._selection.startLineNumber), 0, this._selection.startColumn - 1); let varLeadingWhitespace = lineLeadingWhitespace; variable.snippet.walk(marker => { diff --git a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts index a7c03dca8e0..3ac1d023c21 100644 --- a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts @@ -34,7 +34,7 @@ suite('Snippet Variables Resolver', function () { resolver = new CompositeSnippetVariableResolver([ new ModelBasedVariableResolver(labelService, model), - new SelectionBasedVariableResolver(model, new Selection(1, 1, 1, 1)), + new SelectionBasedVariableResolver(model, new Selection(1, 1, 1, 1), 0), ]); }); @@ -102,24 +102,24 @@ suite('Snippet Variables Resolver', function () { test('editor variables, selection', function () { - resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 2, 3)); + resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 2, 3), 0); assertVariableResolve(resolver, 'TM_SELECTED_TEXT', 'his is line one\nth'); assertVariableResolve(resolver, 'TM_CURRENT_LINE', 'this is line two'); assertVariableResolve(resolver, 'TM_LINE_INDEX', '1'); assertVariableResolve(resolver, 'TM_LINE_NUMBER', '2'); - resolver = new SelectionBasedVariableResolver(model, new Selection(2, 3, 1, 2)); + resolver = new SelectionBasedVariableResolver(model, new Selection(2, 3, 1, 2), 0); assertVariableResolve(resolver, 'TM_SELECTED_TEXT', 'his is line one\nth'); assertVariableResolve(resolver, 'TM_CURRENT_LINE', 'this is line one'); assertVariableResolve(resolver, 'TM_LINE_INDEX', '0'); assertVariableResolve(resolver, 'TM_LINE_NUMBER', '1'); - resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 1, 2)); + resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 1, 2), 0); assertVariableResolve(resolver, 'TM_SELECTED_TEXT', undefined); assertVariableResolve(resolver, 'TM_CURRENT_WORD', 'this'); - resolver = new SelectionBasedVariableResolver(model, new Selection(3, 1, 3, 1)); + resolver = new SelectionBasedVariableResolver(model, new Selection(3, 1, 3, 1), 0); assertVariableResolve(resolver, 'TM_CURRENT_WORD', undefined); }); From 03d601b57c604264f6a6a7c6d9ae5b26a2bba93f Mon Sep 17 00:00:00 2001 From: n-gist Date: Wed, 26 Aug 2020 22:15:10 +0600 Subject: [PATCH 554/736] Revert "Implementation TM_SELECTED_TEXT snippets variable for working with overtyped text" This reverts commit e6519cb820e99c9d4283f395fdee904d6fe4a260. --- src/vs/editor/common/model.ts | 6 ---- src/vs/editor/common/model/editStack.ts | 18 ----------- src/vs/editor/common/model/textModel.ts | 31 ++----------------- .../editor/contrib/snippet/snippetSession.ts | 3 +- .../contrib/snippet/snippetVariables.ts | 20 +++--------- .../snippet/test/snippetVariables.test.ts | 10 +++--- 6 files changed, 12 insertions(+), 76 deletions(-) diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index 6eead87c480..d15e79eab7d 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -1137,12 +1137,6 @@ export interface ITextModel { */ _applyRedo(changes: TextChange[], eol: EndOfLineSequence, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void; - /** - * Returns the text that has just been overtyped - * @internal - */ - getOvertypedText(overtypeIdx: number, undoSearchLimit: number, editSizeLimit: number): string | undefined; - /** * Undo edit operations until the first previous stop point created by `pushStackElement`. * The inverse edit operations will be pushed on the redo stack. diff --git a/src/vs/editor/common/model/editStack.ts b/src/vs/editor/common/model/editStack.ts index a1d3b051d4e..5316fce9e95 100644 --- a/src/vs/editor/common/model/editStack.ts +++ b/src/vs/editor/common/model/editStack.ts @@ -198,24 +198,6 @@ export class SingleModelEditStackElement implements IResourceUndoRedoElement { } } - public getOvertypedTextAtEditingEnd(overtypeIdx: number, editingEnd: number): { text: string | undefined, continuousEditing: boolean, previousEditingEnd: number } { - let continuousEditing = false; - let overtypedText: string | undefined; - const data = (this._data instanceof SingleModelEditStackData ? this._data : SingleModelEditStackData.deserialize(this._data)); - const change = data.changes[overtypeIdx >= data.changes.length ? 0 : overtypeIdx]; - const inserted = change.oldLength === 0; - const deleted = change.newLength === 0; - if (inserted !== deleted) { - // If change is insert, and starts with new line, break the search - if (!(inserted && /^[\r\n]/.test(change.newText))) { - continuousEditing = editingEnd < 0 ? true : change.newEnd === editingEnd; - } - } else if (editingEnd < 0 || change.newEnd === editingEnd) { - overtypedText = change.oldText; - } - return { text: overtypedText, continuousEditing: continuousEditing, previousEditingEnd: change.oldEnd }; - } - public undo(): void { if (URI.isUri(this.model)) { // don't have a model diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 58801ac093e..cb7b34b3451 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -16,7 +16,7 @@ import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import * as model from 'vs/editor/common/model'; -import { EditStack, SingleModelEditStackElement } from 'vs/editor/common/model/editStack'; +import { EditStack } from 'vs/editor/common/model/editStack'; import { guessIndentation } from 'vs/editor/common/model/indentationGuesser'; import { IntervalNode, IntervalTree, getNodeIsInOverviewRuler, recomputeMaxEnd } from 'vs/editor/common/model/intervalTree'; import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder'; @@ -34,7 +34,7 @@ import { VSBufferReadableStream, VSBuffer } from 'vs/base/common/buffer'; import { TokensStore, MultilineTokens, countEOL, MultilineTokens2, TokensStore2 } from 'vs/editor/common/model/tokensStore'; import { Color } from 'vs/base/common/color'; import { EditorTheme } from 'vs/editor/common/view/viewContext'; -import { IUndoRedoService, ResourceEditStackSnapshot, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo'; +import { IUndoRedoService, ResourceEditStackSnapshot } from 'vs/platform/undoRedo/common/undoRedo'; import { TextChange } from 'vs/editor/common/model/textChange'; import { Constants } from 'vs/base/common/uint'; @@ -1491,33 +1491,6 @@ export class TextModel extends Disposable implements model.ITextModel { return (result.reverseEdits === null ? undefined : result.reverseEdits); } - public getOvertypedText(overtypeIdx: number, undoSearchLimit: number, editSizeLimit: number): string | undefined { - const elements = this._undoRedoService.getElements(this.uri).past; - if (elements.length === 0) { - return; - } - - // Cycle through undo elements to find one containing text that was overtyped - let editingEnd = -1; - let elementIndex = elements.length; - do { - const element = elements[--elementIndex]; - if (!(element.type === UndoRedoElementType.Resource && element instanceof SingleModelEditStackElement)) { - return; - } - const searchResult = element.getOvertypedTextAtEditingEnd(overtypeIdx, editingEnd); - if (searchResult.text) { - return searchResult.text; - } - if (!searchResult.continuousEditing || (editingEnd >= 0 && Math.abs(editingEnd - searchResult.previousEditingEnd) > editSizeLimit)) { - return; - } - editingEnd = searchResult.previousEditingEnd; - } while (elementIndex > 0 && --undoSearchLimit > 0); - - return; - } - public undo(): void { this._undoRedoService.undo(this.uri); } diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index 465b5401496..717b223cb4a 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -415,7 +415,6 @@ export class SnippetSession { .map((selection, idx) => ({ selection, idx })) .sort((a, b) => Range.compareRangesUsingStarts(a.selection, b.selection)); - let overtypedIdx = 0; // Makes overtyping snippets working with multiselections for (const { selection, idx } of indexedSelections) { // extend selection with the `overwriteBefore` and `overwriteAfter` and then @@ -450,7 +449,7 @@ export class SnippetSession { snippet.resolveVariables(new CompositeSnippetVariableResolver([ modelBasedVariableResolver, new ClipboardBasedVariableResolver(readClipboardText, idx, indexedSelections.length, editor.getOption(EditorOption.multiCursorPaste) === 'spread'), - new SelectionBasedVariableResolver(model, selection, overtypedIdx++), + new SelectionBasedVariableResolver(model, selection), new CommentBasedVariableResolver(model, selection), new TimeBasedVariableResolver, new WorkspaceBasedVariableResolver(workspaceService), diff --git a/src/vs/editor/contrib/snippet/snippetVariables.ts b/src/vs/editor/contrib/snippet/snippetVariables.ts index a788ca3b47f..3253898bdee 100644 --- a/src/vs/editor/contrib/snippet/snippetVariables.ts +++ b/src/vs/editor/contrib/snippet/snippetVariables.ts @@ -71,8 +71,7 @@ export class SelectionBasedVariableResolver implements VariableResolver { constructor( private readonly _model: ITextModel, - private readonly _selection: Selection, - private readonly _overtypeIdx: number + private readonly _selection: Selection ) { // } @@ -83,25 +82,14 @@ export class SelectionBasedVariableResolver implements VariableResolver { if (name === 'SELECTION' || name === 'TM_SELECTED_TEXT') { let value = this._model.getValueInRange(this._selection) || undefined; - - // If there is no selected text, try to get overtyped text - let overtyped = false; - if (!value) { - const maxTypos = 10; // Allows the user to make 10 typos (delete+insert) when typing the snippet prefix - const maxEditSize = 50; // Limits the searching by edit size less than 50 symbols - value = this._model.getOvertypedText(this._overtypeIdx, 1 + 2 * maxTypos, maxEditSize); - if (value) { - overtyped = true; - } - } - - if (value && (overtyped || (this._selection.startLineNumber !== this._selection.endLineNumber)) && variable.snippet) { + if (value && this._selection.startLineNumber !== this._selection.endLineNumber && variable.snippet) { // Selection is a multiline string which we indentation we now // need to adjust. We compare the indentation of this variable // with the indentation at the editor position and add potential // extra indentation to the value - const lineLeadingWhitespace = overtyped ? '' : getLeadingWhitespace(this._model.getLineContent(this._selection.startLineNumber), 0, this._selection.startColumn - 1); + const line = this._model.getLineContent(this._selection.startLineNumber); + const lineLeadingWhitespace = getLeadingWhitespace(line, 0, this._selection.startColumn - 1); let varLeadingWhitespace = lineLeadingWhitespace; variable.snippet.walk(marker => { diff --git a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts index 3ac1d023c21..a7c03dca8e0 100644 --- a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts @@ -34,7 +34,7 @@ suite('Snippet Variables Resolver', function () { resolver = new CompositeSnippetVariableResolver([ new ModelBasedVariableResolver(labelService, model), - new SelectionBasedVariableResolver(model, new Selection(1, 1, 1, 1), 0), + new SelectionBasedVariableResolver(model, new Selection(1, 1, 1, 1)), ]); }); @@ -102,24 +102,24 @@ suite('Snippet Variables Resolver', function () { test('editor variables, selection', function () { - resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 2, 3), 0); + resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 2, 3)); assertVariableResolve(resolver, 'TM_SELECTED_TEXT', 'his is line one\nth'); assertVariableResolve(resolver, 'TM_CURRENT_LINE', 'this is line two'); assertVariableResolve(resolver, 'TM_LINE_INDEX', '1'); assertVariableResolve(resolver, 'TM_LINE_NUMBER', '2'); - resolver = new SelectionBasedVariableResolver(model, new Selection(2, 3, 1, 2), 0); + resolver = new SelectionBasedVariableResolver(model, new Selection(2, 3, 1, 2)); assertVariableResolve(resolver, 'TM_SELECTED_TEXT', 'his is line one\nth'); assertVariableResolve(resolver, 'TM_CURRENT_LINE', 'this is line one'); assertVariableResolve(resolver, 'TM_LINE_INDEX', '0'); assertVariableResolve(resolver, 'TM_LINE_NUMBER', '1'); - resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 1, 2), 0); + resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 1, 2)); assertVariableResolve(resolver, 'TM_SELECTED_TEXT', undefined); assertVariableResolve(resolver, 'TM_CURRENT_WORD', 'this'); - resolver = new SelectionBasedVariableResolver(model, new Selection(3, 1, 3, 1), 0); + resolver = new SelectionBasedVariableResolver(model, new Selection(3, 1, 3, 1)); assertVariableResolve(resolver, 'TM_CURRENT_WORD', undefined); }); From 19d3347b79ea6066dff028030c279a7f905fdc1c Mon Sep 17 00:00:00 2001 From: n-gist Date: Wed, 26 Aug 2020 22:18:00 +0600 Subject: [PATCH 555/736] Implementation TM_SELECTED_TEXT snippets variable for working with overtyped text (v2) --- .../contrib/snippet/snippetController2.ts | 5 +- .../editor/contrib/snippet/snippetSession.ts | 13 +-- .../contrib/snippet/snippetVariables.ts | 15 +++- .../snippet/test/snippetSession.test.ts | 4 +- .../snippet/test/snippetVariables.test.ts | 10 +-- .../contrib/suggest/suggestController.ts | 10 ++- .../suggest/suggestOvertypingCapturer.ts | 87 +++++++++++++++++++ 7 files changed, 128 insertions(+), 16 deletions(-) create mode 100644 src/vs/editor/contrib/suggest/suggestOvertypingCapturer.ts diff --git a/src/vs/editor/contrib/snippet/snippetController2.ts b/src/vs/editor/contrib/snippet/snippetController2.ts index 8005cf5badf..5c6abcb852a 100644 --- a/src/vs/editor/contrib/snippet/snippetController2.ts +++ b/src/vs/editor/contrib/snippet/snippetController2.ts @@ -18,6 +18,7 @@ import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from ' import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ILogService } from 'vs/platform/log/common/log'; import { SnippetSession } from './snippetSession'; +import { OvertypingCapturer } from 'vs/editor/contrib/suggest/suggestOvertypingCapturer'; export interface ISnippetInsertOptions { overwriteBefore: number; @@ -26,6 +27,7 @@ export interface ISnippetInsertOptions { undoStopBefore: boolean; undoStopAfter: boolean; clipboardText: string | undefined; + overtypingCapturer: OvertypingCapturer | null; } const _defaultOptions: ISnippetInsertOptions = { @@ -34,7 +36,8 @@ const _defaultOptions: ISnippetInsertOptions = { undoStopBefore: true, undoStopAfter: true, adjustWhitespace: true, - clipboardText: undefined + clipboardText: undefined, + overtypingCapturer: null }; export class SnippetController2 implements IEditorContribution { diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index 717b223cb4a..d99b6ba9387 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -23,6 +23,7 @@ import * as colors from 'vs/platform/theme/common/colorRegistry'; import { withNullAsUndefined } from 'vs/base/common/types'; import { ILabelService } from 'vs/platform/label/common/label'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { OvertypingCapturer } from 'vs/editor/contrib/suggest/suggestOvertypingCapturer'; registerThemingParticipant((theme, collector) => { @@ -319,13 +320,15 @@ export interface ISnippetSessionInsertOptions { overwriteAfter: number; adjustWhitespace: boolean; clipboardText: string | undefined; + overtypingCapturer: OvertypingCapturer | null; } const _defaultOptions: ISnippetSessionInsertOptions = { overwriteBefore: 0, overwriteAfter: 0, adjustWhitespace: true, - clipboardText: undefined + clipboardText: undefined, + overtypingCapturer: null }; export class SnippetSession { @@ -382,7 +385,7 @@ export class SnippetSession { return selection; } - static createEditsAndSnippets(editor: IActiveCodeEditor, template: string, overwriteBefore: number, overwriteAfter: number, enforceFinalTabstop: boolean, adjustWhitespace: boolean, clipboardText: string | undefined): { edits: IIdentifiedSingleEditOperation[], snippets: OneSnippet[] } { + static createEditsAndSnippets(editor: IActiveCodeEditor, template: string, overwriteBefore: number, overwriteAfter: number, enforceFinalTabstop: boolean, adjustWhitespace: boolean, clipboardText: string | undefined, overtypingCapturer: OvertypingCapturer | null): { edits: IIdentifiedSingleEditOperation[], snippets: OneSnippet[] } { const edits: IIdentifiedSingleEditOperation[] = []; const snippets: OneSnippet[] = []; @@ -449,7 +452,7 @@ export class SnippetSession { snippet.resolveVariables(new CompositeSnippetVariableResolver([ modelBasedVariableResolver, new ClipboardBasedVariableResolver(readClipboardText, idx, indexedSelections.length, editor.getOption(EditorOption.multiCursorPaste) === 'spread'), - new SelectionBasedVariableResolver(model, selection), + new SelectionBasedVariableResolver(model, selection, idx, overtypingCapturer), new CommentBasedVariableResolver(model, selection), new TimeBasedVariableResolver, new WorkspaceBasedVariableResolver(workspaceService), @@ -496,7 +499,7 @@ export class SnippetSession { } // make insert edit and start with first selections - const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, this._template, this._options.overwriteBefore, this._options.overwriteAfter, false, this._options.adjustWhitespace, this._options.clipboardText); + const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, this._template, this._options.overwriteBefore, this._options.overwriteAfter, false, this._options.adjustWhitespace, this._options.clipboardText, this._options.overtypingCapturer); this._snippets = snippets; this._editor.executeEdits('snippet', edits, undoEdits => { @@ -516,7 +519,7 @@ export class SnippetSession { return; } this._templateMerges.push([this._snippets[0]._nestingLevel, this._snippets[0]._placeholderGroupsIdx, template]); - const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, template, options.overwriteBefore, options.overwriteAfter, true, options.adjustWhitespace, options.clipboardText); + const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, template, options.overwriteBefore, options.overwriteAfter, true, options.adjustWhitespace, options.clipboardText, options.overtypingCapturer); this._editor.executeEdits('snippet', edits, undoEdits => { for (const snippet of this._snippets) { diff --git a/src/vs/editor/contrib/snippet/snippetVariables.ts b/src/vs/editor/contrib/snippet/snippetVariables.ts index 3253898bdee..fd87983cfe4 100644 --- a/src/vs/editor/contrib/snippet/snippetVariables.ts +++ b/src/vs/editor/contrib/snippet/snippetVariables.ts @@ -16,6 +16,7 @@ import { isSingleFolderWorkspaceIdentifier, toWorkspaceIdentifier, WORKSPACE_EXT import { ILabelService } from 'vs/platform/label/common/label'; import { normalizeDriveLetter } from 'vs/base/common/labels'; import { URI } from 'vs/base/common/uri'; +import { OvertypingCapturer } from 'vs/editor/contrib/suggest/suggestOvertypingCapturer'; export const KnownSnippetVariableNames: { [key: string]: true } = Object.freeze({ 'CURRENT_YEAR': true, @@ -71,7 +72,9 @@ export class SelectionBasedVariableResolver implements VariableResolver { constructor( private readonly _model: ITextModel, - private readonly _selection: Selection + private readonly _selection: Selection, + private readonly _selectionIdx: number, + private readonly _overtypingCapturer: OvertypingCapturer | null ) { // } @@ -82,7 +85,15 @@ export class SelectionBasedVariableResolver implements VariableResolver { if (name === 'SELECTION' || name === 'TM_SELECTED_TEXT') { let value = this._model.getValueInRange(this._selection) || undefined; - if (value && this._selection.startLineNumber !== this._selection.endLineNumber && variable.snippet) { + + // If there was no selected text, try to get last overtyped text + let overtyped = false; + if (!value && this._overtypingCapturer) { + value = this._overtypingCapturer.getLastOvertypedText(this._selectionIdx); + overtyped = true; + } + + if (value && (overtyped || this._selection.startLineNumber !== this._selection.endLineNumber) && variable.snippet) { // Selection is a multiline string which we indentation we now // need to adjust. We compare the indentation of this variable // with the indentation at the editor position and add potential diff --git a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts b/src/vs/editor/contrib/snippet/test/snippetSession.test.ts index cedf6be949e..db68a64643e 100644 --- a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetSession.test.ts @@ -127,7 +127,7 @@ suite('SnippetSession', function () { test('snippets, newline NO whitespace adjust', () => { editor.setSelection(new Selection(2, 5, 2, 5)); - const session = new SnippetSession(editor, 'abc\n foo\n bar\n$0', { overwriteBefore: 0, overwriteAfter: 0, adjustWhitespace: false, clipboardText: undefined }); + const session = new SnippetSession(editor, 'abc\n foo\n bar\n$0', { overwriteBefore: 0, overwriteAfter: 0, adjustWhitespace: false, clipboardText: undefined, overtypingCapturer: null }); session.insert(); assert.equal(editor.getModel()!.getValue(), 'function foo() {\n abc\n foo\n bar\nconsole.log(a);\n}'); }); @@ -649,7 +649,7 @@ suite('SnippetSession', function () { assert.ok(actual.equalsSelection(new Selection(1, 9, 1, 12))); editor.setSelections([new Selection(1, 9, 1, 12)]); - new SnippetSession(editor, 'far', { overwriteBefore: 3, overwriteAfter: 0, adjustWhitespace: true, clipboardText: undefined }).insert(); + new SnippetSession(editor, 'far', { overwriteBefore: 3, overwriteAfter: 0, adjustWhitespace: true, clipboardText: undefined, overtypingCapturer: null }).insert(); assert.equal(model.getValue(), 'console.far'); }); }); diff --git a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts index a7c03dca8e0..89b2db3dbef 100644 --- a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts @@ -34,7 +34,7 @@ suite('Snippet Variables Resolver', function () { resolver = new CompositeSnippetVariableResolver([ new ModelBasedVariableResolver(labelService, model), - new SelectionBasedVariableResolver(model, new Selection(1, 1, 1, 1)), + new SelectionBasedVariableResolver(model, new Selection(1, 1, 1, 1), 0, null), ]); }); @@ -102,24 +102,24 @@ suite('Snippet Variables Resolver', function () { test('editor variables, selection', function () { - resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 2, 3)); + resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 2, 3), 0, null); assertVariableResolve(resolver, 'TM_SELECTED_TEXT', 'his is line one\nth'); assertVariableResolve(resolver, 'TM_CURRENT_LINE', 'this is line two'); assertVariableResolve(resolver, 'TM_LINE_INDEX', '1'); assertVariableResolve(resolver, 'TM_LINE_NUMBER', '2'); - resolver = new SelectionBasedVariableResolver(model, new Selection(2, 3, 1, 2)); + resolver = new SelectionBasedVariableResolver(model, new Selection(2, 3, 1, 2), 0, null); assertVariableResolve(resolver, 'TM_SELECTED_TEXT', 'his is line one\nth'); assertVariableResolve(resolver, 'TM_CURRENT_LINE', 'this is line one'); assertVariableResolve(resolver, 'TM_LINE_INDEX', '0'); assertVariableResolve(resolver, 'TM_LINE_NUMBER', '1'); - resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 1, 2)); + resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 1, 2), 0, null); assertVariableResolve(resolver, 'TM_SELECTED_TEXT', undefined); assertVariableResolve(resolver, 'TM_CURRENT_WORD', 'this'); - resolver = new SelectionBasedVariableResolver(model, new Selection(3, 1, 3, 1)); + resolver = new SelectionBasedVariableResolver(model, new Selection(3, 1, 3, 1), 0, null); assertVariableResolve(resolver, 'TM_CURRENT_WORD', undefined); }); diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index 60dd04f3d72..0c24f87f89d 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -34,6 +34,7 @@ import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerServ import { IdleValue } from 'vs/base/common/async'; import { isObject, assertType } from 'vs/base/common/types'; import { CommitCharacterController } from './suggestCommitCharacters'; +import { OvertypingCapturer } from './suggestOvertypingCapturer'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { TrackedRangeStickiness, ITextModel } from 'vs/editor/common/model'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; @@ -112,6 +113,7 @@ export class SuggestController implements IEditorContribution { private readonly _alternatives: IdleValue; private readonly _lineSuffix = new MutableDisposable(); private readonly _toDispose = new DisposableStore(); + private readonly _overtypingCapturer: IdleValue; constructor( editor: ICodeEditor, @@ -203,6 +205,11 @@ export class SuggestController implements IEditorContribution { return widget; })); + // Wire up text overtyping capture + this._overtypingCapturer = this._toDispose.add(new IdleValue(() => { + return this._toDispose.add(new OvertypingCapturer(this.editor, this.widget.value)); + })); + this._alternatives = this._toDispose.add(new IdleValue(() => { return this._toDispose.add(new SuggestAlternatives(this.editor, this._contextKeyService)); })); @@ -361,7 +368,8 @@ export class SuggestController implements IEditorContribution { undoStopBefore: false, undoStopAfter: false, adjustWhitespace: !(item.completion.insertTextRules! & CompletionItemInsertTextRule.KeepWhitespace), - clipboardText: event.model.clipboardText + clipboardText: event.model.clipboardText, + overtypingCapturer: this._overtypingCapturer.value }); if (!(flags & InsertFlags.NoAfterUndoStop)) { diff --git a/src/vs/editor/contrib/suggest/suggestOvertypingCapturer.ts b/src/vs/editor/contrib/suggest/suggestOvertypingCapturer.ts new file mode 100644 index 00000000000..36ffbf946a4 --- /dev/null +++ b/src/vs/editor/contrib/suggest/suggestOvertypingCapturer.ts @@ -0,0 +1,87 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { SuggestWidget } from './suggestWidget'; + +export class OvertypingCapturer implements IDisposable { + + private readonly _maxSelectionLength = 1000000; + private readonly _disposables = new DisposableStore(); + private readonly _editor: ICodeEditor; + + private _lastOvertyped: string[]; + private _holdCurrent: boolean; + + constructor(editor: ICodeEditor, widget: SuggestWidget) { + this._editor = editor; + this._lastOvertyped = new Array(0); + this._holdCurrent = false; + + this._disposables.add(this._editor.onWillType(text => { + if (this._holdCurrent) { + return; + } + const selections = this._editor.getSelections(); + if (!selections) { + return; + } + + const selectionsLength = selections.length; + + // Check if it will overtype any selections + let willOvertype = false; + for (let i = 0; i < selectionsLength; i++) { + if (!selections[i].isEmpty()) { + willOvertype = true; + break; + } + } + if (!willOvertype) { + return; + } + + const model = this._editor.getModel(); + if (!model) { + return; + } + + // Check for overtyping capturer restrictions + for (let i = 0; i < selectionsLength; i++) { + if (model.getValueLengthInRange(selections[i]) > this._maxSelectionLength) { + return; + } + } + + this._lastOvertyped = new Array(selectionsLength); + for (let i = 0; i < selectionsLength; i++) { + this._lastOvertyped[i] = model.getValueInRange(selections[i]); + } + })); + + this._disposables.add(widget.onDidShow(() => { + this._holdCurrent = true; + })); + + this._disposables.add(widget.onDidHide(() => { + if (this._lastOvertyped.length > 0) { + this._lastOvertyped = new Array(0); + } + this._holdCurrent = false; + })); + } + + getLastOvertypedText(idx: number): string | undefined { + if (idx < this._lastOvertyped.length && this._lastOvertyped[idx].length > 0) { + return this._lastOvertyped[idx]; + } + return undefined; + } + + dispose() { + this._disposables.dispose(); + } +} From f9d74e9be9562659ad3d0f14d47e484663ad297a Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 26 Aug 2020 18:18:39 +0200 Subject: [PATCH 556/736] Firefox does not support navigator.clipboard.readText() --- src/vs/editor/contrib/clipboard/clipboard.ts | 87 ++++++++++---------- 1 file changed, 45 insertions(+), 42 deletions(-) diff --git a/src/vs/editor/contrib/clipboard/clipboard.ts b/src/vs/editor/contrib/clipboard/clipboard.ts index cbdeb7424d3..6860a7bb610 100644 --- a/src/vs/editor/contrib/clipboard/clipboard.ts +++ b/src/vs/editor/contrib/clipboard/clipboard.ts @@ -25,6 +25,9 @@ const supportsCut = (platform.isNative || document.queryCommandSupported('cut')) const supportsCopy = (platform.isNative || document.queryCommandSupported('copy')); // IE and Edge have trouble with setting html content in clipboard const supportsCopyWithSyntaxHighlighting = (supportsCopy && !browser.isEdge); +// Firefox only supports navigator.clipboard.readText() in browser extensions. +// See https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/readText#Browser_compatibility +const supportsPaste = (browser.isFirefox ? document.queryCommandSupported('paste') : true); function registerCommand(command: T): T { command.register(); @@ -92,7 +95,7 @@ export const CopyAction = supportsCopy ? registerCommand(new MultiCommand({ }] })) : undefined; -export const PasteAction = registerCommand(new MultiCommand({ +export const PasteAction = supportsPaste ? registerCommand(new MultiCommand({ id: 'editor.action.clipboardPasteAction', precondition: undefined, kbOpts: ( @@ -122,7 +125,7 @@ export const PasteAction = registerCommand(new MultiCommand({ title: nls.localize('actions.clipboard.pasteLabel', "Paste"), order: 1 }] -})); +})) : undefined; class ExecCommandCopyWithSyntaxHighlightingAction extends EditorAction { @@ -190,51 +193,51 @@ function registerExecCommandImpl(target: MultiCommand | undefined, browserComman registerExecCommandImpl(CutAction, 'cut'); registerExecCommandImpl(CopyAction, 'copy'); -// 1. Paste: handle case when focus is in editor. -PasteAction.addImplementation(10000, (accessor: ServicesAccessor, args: any) => { - const codeEditorService = accessor.get(ICodeEditorService); - const clipboardService = accessor.get(IClipboardService); +if (PasteAction) { + // 1. Paste: handle case when focus is in editor. + PasteAction.addImplementation(10000, (accessor: ServicesAccessor, args: any) => { + const codeEditorService = accessor.get(ICodeEditorService); + const clipboardService = accessor.get(IClipboardService); - // Only if editor text focus (i.e. not if editor has widget focus). - const focusedEditor = codeEditorService.getFocusedCodeEditor(); - if (focusedEditor && focusedEditor.hasTextFocus()) { - const result = document.execCommand('paste'); - // Web: certain browsers do not allow document.execCommand('paste') - // and as such we implement a workaround via the clipboard service - // Refs: https://github.com/microsoft/vscode/issues/82604 - if (!result && platform.isWeb) { - (async () => { - const clipboardText = await clipboardService.readText(); - if (clipboardText !== '') { - const metadata = InMemoryClipboardMetadataManager.INSTANCE.get(clipboardText); - let pasteOnNewLine = false; - let multicursorText: string[] | null = null; - let mode: string | null = null; - if (metadata) { - pasteOnNewLine = (focusedEditor.getOption(EditorOption.emptySelectionClipboard) && !!metadata.isFromEmptySelection); - multicursorText = (typeof metadata.multicursorText !== 'undefined' ? metadata.multicursorText : null); - mode = metadata.mode; + // Only if editor text focus (i.e. not if editor has widget focus). + const focusedEditor = codeEditorService.getFocusedCodeEditor(); + if (focusedEditor && focusedEditor.hasTextFocus()) { + const result = document.execCommand('paste'); + // Use the clipboard service if document.execCommand('paste') was not successful + if (!result && platform.isWeb) { + (async () => { + const clipboardText = await clipboardService.readText(); + if (clipboardText !== '') { + const metadata = InMemoryClipboardMetadataManager.INSTANCE.get(clipboardText); + let pasteOnNewLine = false; + let multicursorText: string[] | null = null; + let mode: string | null = null; + if (metadata) { + pasteOnNewLine = (focusedEditor.getOption(EditorOption.emptySelectionClipboard) && !!metadata.isFromEmptySelection); + multicursorText = (typeof metadata.multicursorText !== 'undefined' ? metadata.multicursorText : null); + mode = metadata.mode; + } + focusedEditor.trigger('keyboard', Handler.Paste, { + text: clipboardText, + pasteOnNewLine, + multicursorText, + mode + }); } - focusedEditor.trigger('keyboard', Handler.Paste, { - text: clipboardText, - pasteOnNewLine, - multicursorText, - mode - }); - } - })(); + })(); + return true; + } return true; } - return true; - } - return false; -}); + return false; + }); -// 2. Paste: (default) handle case when focus is somewhere else. -PasteAction.addImplementation(0, (accessor: ServicesAccessor, args: any) => { - document.execCommand('paste'); - return true; -}); + // 2. Paste: (default) handle case when focus is somewhere else. + PasteAction.addImplementation(0, (accessor: ServicesAccessor, args: any) => { + document.execCommand('paste'); + return true; + }); +} if (supportsCopyWithSyntaxHighlighting) { registerEditorAction(ExecCommandCopyWithSyntaxHighlightingAction); From 88029a6643b9d4da4023721e7c077aaa89c10e16 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 26 Aug 2020 09:26:07 -0700 Subject: [PATCH 557/736] debug: bump js-debug-companion --- product.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product.json b/product.json index c6c74ce1f22..3b18619d1fe 100644 --- a/product.json +++ b/product.json @@ -76,7 +76,7 @@ }, { "name": "ms-vscode.js-debug-companion", - "version": "1.0.4", + "version": "1.0.5", "repo": "https://github.com/microsoft/vscode-js-debug-companion", "metadata": { "id": "99cb0b7f-7354-4278-b8da-6cc79972169d", From 0a318b069f303961a0ac96114fbbdb535ccca82d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 26 Aug 2020 17:46:05 +0200 Subject: [PATCH 558/736] refine Dto-type --- src/vs/base/common/types.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/base/common/types.ts b/src/vs/base/common/types.ts index ee9172a4f84..3a34cb7a1c6 100644 --- a/src/vs/base/common/types.ts +++ b/src/vs/base/common/types.ts @@ -260,11 +260,11 @@ export type UriDto = { [K in keyof T]: T[K] extends URI * drops all functions. * todo@joh use toJSON-results */ -export type Dto = { [K in keyof T]: T[K] extends URI - ? UriComponents +export type Dto = { [K in keyof T]: T[K] extends { toJSON(): infer U } + ? U : T[K] extends Function ? never - : UriDto }; + : Dto }; export function NotImplementedProxy(name: string): { new(): T } { From 35ba71b741a936779de2d76c8433f2e09578e43a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 26 Aug 2020 18:23:59 +0200 Subject: [PATCH 559/736] add Revived-type for generic revive-function --- src/vs/base/common/marshalling.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/vs/base/common/marshalling.ts b/src/vs/base/common/marshalling.ts index e76ba91738f..4af920357fd 100644 --- a/src/vs/base/common/marshalling.ts +++ b/src/vs/base/common/marshalling.ts @@ -5,7 +5,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { regExpFlags } from 'vs/base/common/strings'; -import { URI } from 'vs/base/common/uri'; +import { URI, UriComponents } from 'vs/base/common/uri'; export function stringify(obj: any): string { return JSON.stringify(obj, replacer); @@ -33,7 +33,15 @@ function replacer(key: string, value: any): any { return value; } -export function revive(obj: any, depth = 0): any { + +type Deserialize = T extends UriComponents ? URI + : T extends object + ? Revived + : T; + +export type Revived = { [K in keyof T]: Deserialize }; + +export function revive(obj: any, depth = 0): Revived { if (!obj || depth > 200) { return obj; } @@ -41,15 +49,15 @@ export function revive(obj: any, depth = 0): any { if (typeof obj === 'object') { switch ((obj).$mid) { - case 1: return URI.revive(obj); - case 2: return new RegExp(obj.source, obj.flags); + case 1: return URI.revive(obj); + case 2: return new RegExp(obj.source, obj.flags); } if ( obj instanceof VSBuffer || obj instanceof Uint8Array ) { - return obj; + return obj; } if (Array.isArray(obj)) { From 600d8a51cba7b67f140c7466be7a662992c27792 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 26 Aug 2020 18:24:29 +0200 Subject: [PATCH 560/736] tweak workspace edit dto and handling https://github.com/microsoft/vscode/issues/105283 --- .../api/browser/mainThreadEditors.ts | 25 +++++-- .../workbench/api/common/extHost.protocol.ts | 7 ++ .../workbench/api/common/extHostCommands.ts | 2 +- .../common/extHostDocumentSaveParticipant.ts | 3 +- .../api/common/extHostTypeConverters.ts | 4 +- src/vs/workbench/api/common/extHostTypes.ts | 67 ++++++++++--------- .../browser/api/extHostTextEditors.test.ts | 12 ++-- .../test/browser/api/extHostTypes.test.ts | 12 ++-- .../browser/api/mainThreadEditors.test.ts | 11 +-- 9 files changed, 89 insertions(+), 54 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadEditors.ts b/src/vs/workbench/api/browser/mainThreadEditors.ts index bdcc94c864e..6ea3c01a77c 100644 --- a/src/vs/workbench/api/browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadEditors.ts @@ -8,7 +8,7 @@ import { disposed } from 'vs/base/common/errors'; import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle'; import { equals as objectEquals } from 'vs/base/common/objects'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { IBulkEditService, ResourceEdit } from 'vs/editor/browser/services/bulkEditService'; +import { IBulkEditService, ResourceEdit, ResourceFileEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IRange } from 'vs/editor/common/core/range'; import { ISelection } from 'vs/editor/common/core/selection'; @@ -20,7 +20,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { IOpenerService } from 'vs/platform/opener/common/opener'; import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/browser/mainThreadDocumentsAndEditors'; import { MainThreadTextEditor } from 'vs/workbench/api/browser/mainThreadEditor'; -import { ExtHostContext, ExtHostEditorsShape, IApplyEditsOptions, IExtHostContext, ITextDocumentShowOptions, ITextEditorConfigurationUpdate, ITextEditorPositionData, IUndoStopOptions, MainThreadTextEditorsShape, TextEditorRevealType, IWorkspaceEditDto, reviveWorkspaceEditDto } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostContext, ExtHostEditorsShape, IApplyEditsOptions, IExtHostContext, ITextDocumentShowOptions, ITextEditorConfigurationUpdate, ITextEditorPositionData, IUndoStopOptions, MainThreadTextEditorsShape, TextEditorRevealType, IWorkspaceEditDto, WorkspaceEditType } from 'vs/workbench/api/common/extHost.protocol'; import { EditorViewColumn, editorGroupToViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -29,6 +29,23 @@ import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { revive } from 'vs/base/common/marshalling'; + +function reviveWorkspaceEditDto2(data: IWorkspaceEditDto | undefined): ResourceEdit[] { + if (!data?.edits) { + return []; + } + + const result: ResourceEdit[] = []; + for (let edit of revive(data).edits) { + if (edit._type === WorkspaceEditType.File) { + result.push(new ResourceFileEdit(edit.oldUri, edit.newUri, edit.options, edit.metadata)); + } else if (edit._type === WorkspaceEditType.Text) { + result.push(new ResourceTextEdit(edit.resource, edit.edit, edit.modelVersionId, edit.metadata)); + } + } + return result; +} export class MainThreadTextEditors implements MainThreadTextEditorsShape { @@ -222,8 +239,8 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { } $tryApplyWorkspaceEdit(dto: IWorkspaceEditDto): Promise { - const { edits } = reviveWorkspaceEditDto(dto)!; - return this._bulkEditService.apply(ResourceEdit.convert({ edits })).then(() => true, _err => false); + const edits = reviveWorkspaceEditDto2(dto); + return this._bulkEditService.apply(edits).then(() => true, _err => false); } $tryInsertSnippet(id: string, template: string, ranges: readonly IRange[], opts: IUndoStopOptions): Promise { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index e09f81490e8..e0415cf4584 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1232,7 +1232,13 @@ export interface IWorkspaceEditEntryMetadataDto { iconPath?: { id: string } | UriComponents | { light: UriComponents, dark: UriComponents }; } +export const enum WorkspaceEditType { + File = 1, + Text = 2, +} + export interface IWorkspaceFileEditDto { + _type: WorkspaceEditType.File; oldUri?: UriComponents; newUri?: UriComponents; options?: modes.WorkspaceFileEditOptions @@ -1240,6 +1246,7 @@ export interface IWorkspaceFileEditDto { } export interface IWorkspaceTextEditDto { + _type: WorkspaceEditType.Text; resource: UriComponents; edit: modes.TextEdit; modelVersionId?: number; diff --git a/src/vs/workbench/api/common/extHostCommands.ts b/src/vs/workbench/api/common/extHostCommands.ts index 83fc7bc31ac..8dc69cb7abd 100644 --- a/src/vs/workbench/api/common/extHostCommands.ts +++ b/src/vs/workbench/api/common/extHostCommands.ts @@ -141,7 +141,7 @@ export class ExtHostCommands implements ExtHostCommandsShape { try { const result = await this._proxy.$executeCommand(id, toArgs, retry); - return revive(result); + return revive(result); } catch (e) { // Rerun the command when it wasn't known, had arguments, and when retry // is enabled. We do this because the command might be registered inside diff --git a/src/vs/workbench/api/common/extHostDocumentSaveParticipant.ts b/src/vs/workbench/api/common/extHostDocumentSaveParticipant.ts index 1eb5b7bc391..5613e66db18 100644 --- a/src/vs/workbench/api/common/extHostDocumentSaveParticipant.ts +++ b/src/vs/workbench/api/common/extHostDocumentSaveParticipant.ts @@ -6,7 +6,7 @@ import { Event } from 'vs/base/common/event'; import { URI, UriComponents } from 'vs/base/common/uri'; import { illegalState } from 'vs/base/common/errors'; -import { ExtHostDocumentSaveParticipantShape, MainThreadTextEditorsShape, IWorkspaceEditDto } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostDocumentSaveParticipantShape, MainThreadTextEditorsShape, IWorkspaceEditDto, WorkspaceEditType } from 'vs/workbench/api/common/extHost.protocol'; import { TextEdit } from 'vs/workbench/api/common/extHostTypes'; import { Range, TextDocumentSaveReason, EndOfLine } from 'vs/workbench/api/common/extHostTypeConverters'; import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; @@ -146,6 +146,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic if (Array.isArray(value) && (value).every(e => e instanceof TextEdit)) { for (const { newText, newEol, range } of value) { dto.edits.push({ + _type: WorkspaceEditType.Text, resource: document.uri, edit: { range: range && Range.from(range), diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 66b48dbacd8..c5712ebba6d 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -513,9 +513,10 @@ export namespace WorkspaceEdit { if (value instanceof types.WorkspaceEdit) { for (let entry of value.allEntries()) { - if (entry._type === 1) { + if (entry._type === types.FileEditType.File) { // file operation result.edits.push({ + _type: extHostProtocol.WorkspaceEditType.File, oldUri: entry.from, newUri: entry.to, options: entry.options, @@ -526,6 +527,7 @@ export namespace WorkspaceEdit { // text edits const doc = documents?.getDocument(entry.uri); result.edits.push({ + _type: extHostProtocol.WorkspaceEditType.Text, resource: entry.uri, edit: TextEdit.from(entry.edit), modelVersionId: doc?.version, diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 5d6b6dc15a2..1ab689c1850 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -3,11 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { coalesce, equals } from 'vs/base/common/arrays'; +import { coalesceInPlace, equals } from 'vs/base/common/arrays'; import { escapeCodicons } from 'vs/base/common/codicons'; import { illegalArgument } from 'vs/base/common/errors'; import { IRelativePattern } from 'vs/base/common/glob'; import { isMarkdownString } from 'vs/base/common/htmlContent'; +import { ResourceMap } from 'vs/base/common/map'; import { startsWith } from 'vs/base/common/strings'; import { isStringArray } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; @@ -575,8 +576,13 @@ export interface IFileOperationOptions { recursive?: boolean; } +export const enum FileEditType { + File = 1, + Text = 2 +} + export interface IFileOperation { - _type: 1; + _type: FileEditType.File; from?: URI; to?: URI; options?: IFileOperationOptions; @@ -584,7 +590,7 @@ export interface IFileOperation { } export interface IFileTextEdit { - _type: 2; + _type: FileEditType.Text; uri: URI; edit: TextEdit; metadata?: vscode.WorkspaceEditEntryMetadata; @@ -593,22 +599,31 @@ export interface IFileTextEdit { @es5ClassCompat export class WorkspaceEdit implements vscode.WorkspaceEdit { - private _edits = new Array(); + private readonly _edits = new Array(); + + + allEntries(): ReadonlyArray { + return this._edits; + } + + // --- file renameFile(from: vscode.Uri, to: vscode.Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean; }, metadata?: vscode.WorkspaceEditEntryMetadata): void { - this._edits.push({ _type: 1, from, to, options, metadata }); + this._edits.push({ _type: FileEditType.File, from, to, options, metadata }); } createFile(uri: vscode.Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean; }, metadata?: vscode.WorkspaceEditEntryMetadata): void { - this._edits.push({ _type: 1, from: undefined, to: uri, options, metadata }); + this._edits.push({ _type: FileEditType.File, from: undefined, to: uri, options, metadata }); } deleteFile(uri: vscode.Uri, options?: { recursive?: boolean, ignoreIfNotExists?: boolean; }, metadata?: vscode.WorkspaceEditEntryMetadata): void { - this._edits.push({ _type: 1, from: uri, to: undefined, options, metadata }); + this._edits.push({ _type: FileEditType.File, from: uri, to: undefined, options, metadata }); } + // --- text + replace(uri: URI, range: Range, newText: string, metadata?: vscode.WorkspaceEditEntryMetadata): void { - this._edits.push({ _type: 2, uri, edit: new TextEdit(range, newText), metadata }); + this._edits.push({ _type: FileEditType.Text, uri, edit: new TextEdit(range, newText), metadata }); } insert(resource: URI, position: Position, newText: string, metadata?: vscode.WorkspaceEditEntryMetadata): void { @@ -619,8 +634,10 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { this.replace(resource, range, '', metadata); } + // --- text (Maplike) + has(uri: URI): boolean { - return this._edits.some(edit => edit._type === 2 && edit.uri.toString() === uri.toString()); + return this._edits.some(edit => edit._type === FileEditType.Text && edit.uri.toString() === uri.toString()); } set(uri: URI, edits: TextEdit[]): void { @@ -628,16 +645,16 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { // remove all text edits for `uri` for (let i = 0; i < this._edits.length; i++) { const element = this._edits[i]; - if (element._type === 2 && element.uri.toString() === uri.toString()) { + if (element._type === FileEditType.Text && element.uri.toString() === uri.toString()) { this._edits[i] = undefined!; // will be coalesced down below } } - this._edits = coalesce(this._edits); + coalesceInPlace(this._edits); } else { // append edit to the end for (const edit of edits) { if (edit) { - this._edits.push({ _type: 2, uri, edit }); + this._edits.push({ _type: FileEditType.Text, uri, edit }); } } } @@ -646,7 +663,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { get(uri: URI): TextEdit[] { const res: TextEdit[] = []; for (let candidate of this._edits) { - if (candidate._type === 2 && candidate.uri.toString() === uri.toString()) { + if (candidate._type === FileEditType.Text && candidate.uri.toString() === uri.toString()) { res.push(candidate.edit); } } @@ -654,13 +671,13 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { } entries(): [URI, TextEdit[]][] { - const textEdits = new Map(); + const textEdits = new ResourceMap<[URI, TextEdit[]]>(); for (let candidate of this._edits) { - if (candidate._type === 2) { - let textEdit = textEdits.get(candidate.uri.toString()); + if (candidate._type === FileEditType.Text) { + let textEdit = textEdits.get(candidate.uri); if (!textEdit) { textEdit = [candidate.uri, []]; - textEdits.set(candidate.uri.toString(), textEdit); + textEdits.set(candidate.uri, textEdit); } textEdit[1].push(candidate.edit); } @@ -668,22 +685,6 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { return [...textEdits.values()]; } - allEntries(): ReadonlyArray { - return this._edits; - } - - // _allEntries(): ([URI, TextEdit] | [URI?, URI?, IFileOperationOptions?])[] { - // const res: ([URI, TextEdit] | [URI?, URI?, IFileOperationOptions?])[] = []; - // for (let edit of this._edits) { - // if (edit._type === 1) { - // res.push([edit.from, edit.to, edit.options]); - // } else { - // res.push([edit.uri, edit.edit]); - // } - // } - // return res; - // } - get size(): number { return this.entries().length; } diff --git a/src/vs/workbench/test/browser/api/extHostTextEditors.test.ts b/src/vs/workbench/test/browser/api/extHostTextEditors.test.ts index 4d4a90b1ec1..0d94ec01908 100644 --- a/src/vs/workbench/test/browser/api/extHostTextEditors.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTextEditors.test.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; -import { MainContext, MainThreadTextEditorsShape, IWorkspaceEditDto } from 'vs/workbench/api/common/extHost.protocol'; +import { MainContext, MainThreadTextEditorsShape, IWorkspaceEditDto, WorkspaceEditType } from 'vs/workbench/api/common/extHost.protocol'; import { URI } from 'vs/base/common/uri'; import { mock } from 'vs/base/test/common/mock'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { SingleProxyRPCProtocol, TestRPCProtocol } from 'vs/workbench/test/browser/api/testRPCProtocol'; import { ExtHostEditors } from 'vs/workbench/api/common/extHostTextEditors'; -import { WorkspaceTextEdit } from 'vs/editor/common/modes'; import { NullLogService } from 'vs/platform/log/common/log'; +import { assertType } from 'vs/base/common/types'; suite('ExtHostTextEditors.applyWorkspaceEdit', () => { @@ -48,7 +48,9 @@ suite('ExtHostTextEditors.applyWorkspaceEdit', () => { edit.replace(resource, new extHostTypes.Range(0, 0, 0, 0), 'hello'); await editors.applyWorkspaceEdit(edit); assert.equal(workspaceResourceEdits.edits.length, 1); - assert.equal((workspaceResourceEdits.edits[0]).modelVersionId, 1337); + const [first] = workspaceResourceEdits.edits; + assertType(first._type === WorkspaceEditType.Text); + assert.equal(first.modelVersionId, 1337); }); test('does not use version id if document is not available', async () => { @@ -56,7 +58,9 @@ suite('ExtHostTextEditors.applyWorkspaceEdit', () => { edit.replace(URI.parse('foo:bar2'), new extHostTypes.Range(0, 0, 0, 0), 'hello'); await editors.applyWorkspaceEdit(edit); assert.equal(workspaceResourceEdits.edits.length, 1); - assert.ok(typeof (workspaceResourceEdits.edits[0]).modelVersionId === 'undefined'); + const [first] = workspaceResourceEdits.edits; + assertType(first._type === WorkspaceEditType.Text); + assert.ok(typeof first.modelVersionId === 'undefined'); }); }); diff --git a/src/vs/workbench/test/browser/api/extHostTypes.test.ts b/src/vs/workbench/test/browser/api/extHostTypes.test.ts index 92e9616c1a8..fe6998e08c6 100644 --- a/src/vs/workbench/test/browser/api/extHostTypes.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTypes.test.ts @@ -388,17 +388,17 @@ suite('ExtHostTypes', function () { assert.equal(all.length, 4); const [first, second, third, fourth] = all; - assertType(first._type === 2); + assertType(first._type === types.FileEditType.Text); assert.equal(first.uri.toString(), 'foo:a'); - assertType(second._type === 1); + assertType(second._type === types.FileEditType.File); assert.equal(second.from!.toString(), 'foo:a'); assert.equal(second.to!.toString(), 'foo:b'); - assertType(third._type === 2); + assertType(third._type === types.FileEditType.Text); assert.equal(third.uri.toString(), 'foo:a'); - assertType(fourth._type === 2); + assertType(fourth._type === types.FileEditType.Text); assert.equal(fourth.uri.toString(), 'foo:b'); }); @@ -411,8 +411,8 @@ suite('ExtHostTypes', function () { assert.equal(edit.allEntries().length, 2); let [first, second] = edit.allEntries(); - assertType(first._type === 2); - assertType(second._type === 2); + assertType(first._type === types.FileEditType.Text); + assertType(second._type === types.FileEditType.Text); assert.equal(first.edit.newText, 'Hello'); assert.equal(second.edit.newText, 'Foo'); }); diff --git a/src/vs/workbench/test/browser/api/mainThreadEditors.test.ts b/src/vs/workbench/test/browser/api/mainThreadEditors.test.ts index f4d82ca1786..b413ec9fa5d 100644 --- a/src/vs/workbench/test/browser/api/mainThreadEditors.test.ts +++ b/src/vs/workbench/test/browser/api/mainThreadEditors.test.ts @@ -10,7 +10,7 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/ import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { TestCodeEditorService } from 'vs/editor/test/browser/editorTestServices'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { ExtHostDocumentsAndEditorsShape, ExtHostContext, ExtHostDocumentsShape, IWorkspaceTextEditDto } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostDocumentsAndEditorsShape, ExtHostContext, ExtHostDocumentsShape, IWorkspaceTextEditDto, WorkspaceEditType } from 'vs/workbench/api/common/extHost.protocol'; import { mock } from 'vs/base/test/common/mock'; import { Event } from 'vs/base/common/event'; import { MainThreadTextEditors } from 'vs/workbench/api/browser/mainThreadEditors'; @@ -170,6 +170,7 @@ suite('MainThreadEditors', () => { let model = modelService.createModel('something', null, resource); let workspaceResourceEdit: IWorkspaceTextEditDto = { + _type: WorkspaceEditType.Text, resource: resource, modelVersionId: model.getVersionId(), edit: { @@ -191,6 +192,7 @@ suite('MainThreadEditors', () => { let model = modelService.createModel('something', null, resource); let workspaceResourceEdit1: IWorkspaceTextEditDto = { + _type: WorkspaceEditType.Text, resource: resource, modelVersionId: model.getVersionId(), edit: { @@ -199,6 +201,7 @@ suite('MainThreadEditors', () => { } }; let workspaceResourceEdit2: IWorkspaceTextEditDto = { + _type: WorkspaceEditType.Text, resource: resource, modelVersionId: model.getVersionId(), edit: { @@ -221,9 +224,9 @@ suite('MainThreadEditors', () => { test(`applyWorkspaceEdit with only resource edit`, () => { return editors.$tryApplyWorkspaceEdit({ edits: [ - { oldUri: resource, newUri: resource, options: undefined }, - { oldUri: undefined, newUri: resource, options: undefined }, - { oldUri: resource, newUri: undefined, options: undefined } + { _type: WorkspaceEditType.File, oldUri: resource, newUri: resource, options: undefined }, + { _type: WorkspaceEditType.File, oldUri: undefined, newUri: resource, options: undefined }, + { _type: WorkspaceEditType.File, oldUri: resource, newUri: undefined, options: undefined } ] }).then((result) => { assert.equal(result, true); From 1fe56130bd7812b8a99c7bc49ae320342f98894f Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 25 Aug 2020 15:19:39 -0700 Subject: [PATCH 561/736] detached overflow widgets for diff editor. --- src/vs/editor/browser/editorBrowser.ts | 8 ++++++ .../editor/browser/widget/diffEditorWidget.ts | 15 +++++------ src/vs/monaco.d.ts | 8 ++++++ src/vs/vscode.proposed.d.ts | 5 +++- .../notebook/browser/diff/cellComponents.ts | 25 +++++++++++++------ .../contrib/notebook/browser/diff/common.ts | 1 + .../browser/diff/notebookTextDiffEditor.ts | 15 +++++++---- 7 files changed, 56 insertions(+), 21 deletions(-) diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index a5d5511f041..aa1ea51f927 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -348,6 +348,14 @@ export interface IEditorConstructionOptions extends IEditorOptions { overflowWidgetsDomNode?: HTMLElement; } +export interface IDiffEditorConstructionOptions extends IDiffEditorOptions { + /** + * Place overflow widgets inside an external DOM node. + * Defaults to an internal DOM node. + */ + overflowWidgetsDomNode?: HTMLElement; +} + /** * A rich code editor. */ diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index 118198424c0..d7206b4d844 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -230,7 +230,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE constructor( domElement: HTMLElement, - options: IDiffEditorOptions, + options: editorBrowser.IDiffEditorConstructionOptions, @IClipboardService clipboardService: IClipboardService, @IEditorWorkerService editorWorkerService: IEditorWorkerService, @IContextKeyService contextKeyService: IContextKeyService, @@ -487,7 +487,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._layoutOverviewRulers(); } - private _createLeftHandSideEditor(options: IDiffEditorOptions, instantiationService: IInstantiationService): CodeEditorWidget { + private _createLeftHandSideEditor(options: editorBrowser.IDiffEditorConstructionOptions, instantiationService: IInstantiationService): CodeEditorWidget { const editor = this._createInnerEditor(instantiationService, this._originalDomNode, this._adjustOptionsForLeftHandSide(options, this._originalIsEditable, this._originalCodeLens)); this._register(editor.onDidScrollChange((e) => { @@ -532,7 +532,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE return editor; } - private _createRightHandSideEditor(options: IDiffEditorOptions, instantiationService: IInstantiationService): CodeEditorWidget { + private _createRightHandSideEditor(options: editorBrowser.IDiffEditorConstructionOptions, instantiationService: IInstantiationService): CodeEditorWidget { const editor = this._createInnerEditor(instantiationService, this._modifiedDomNode, this._adjustOptionsForRightHandSide(options, this._modifiedCodeLens)); this._register(editor.onDidScrollChange((e) => { @@ -1089,8 +1089,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } } - private _adjustOptionsForSubEditor(options: IDiffEditorOptions): IDiffEditorOptions { - let clonedOptions: IDiffEditorOptions = objects.deepClone(options || {}); + private _adjustOptionsForSubEditor(options: editorBrowser.IDiffEditorConstructionOptions): editorBrowser.IDiffEditorConstructionOptions { + let clonedOptions: editorBrowser.IDiffEditorConstructionOptions = objects.deepClone(options || {}); clonedOptions.inDiffEditor = true; clonedOptions.wordWrap = 'off'; clonedOptions.wordWrapMinified = false; @@ -1100,6 +1100,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE clonedOptions.folding = false; clonedOptions.codeLens = false; clonedOptions.fixedOverflowWidgets = true; + clonedOptions.overflowWidgetsDomNode = options.overflowWidgetsDomNode; // clonedOptions.lineDecorationsWidth = '2ch'; if (!clonedOptions.minimap) { clonedOptions.minimap = {}; @@ -1108,7 +1109,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE return clonedOptions; } - private _adjustOptionsForLeftHandSide(options: IDiffEditorOptions, isEditable: boolean, isCodeLensEnabled: boolean): IEditorOptions { + private _adjustOptionsForLeftHandSide(options: editorBrowser.IDiffEditorConstructionOptions, isEditable: boolean, isCodeLensEnabled: boolean): editorBrowser.IEditorConstructionOptions { let result = this._adjustOptionsForSubEditor(options); if (isCodeLensEnabled) { result.codeLens = true; @@ -1118,7 +1119,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE return result; } - private _adjustOptionsForRightHandSide(options: IDiffEditorOptions, isCodeLensEnabled: boolean): IEditorOptions { + private _adjustOptionsForRightHandSide(options: editorBrowser.IDiffEditorConstructionOptions, isCodeLensEnabled: boolean): editorBrowser.IEditorConstructionOptions { let result = this._adjustOptionsForSubEditor(options); if (isCodeLensEnabled) { result.codeLens = true; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 54e239c8c65..7f438e97ee9 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4414,6 +4414,14 @@ declare namespace monaco.editor { overflowWidgetsDomNode?: HTMLElement; } + export interface IDiffEditorConstructionOptions extends IDiffEditorOptions { + /** + * Place overflow widgets inside an external DOM node. + * Defaults to an internal DOM node. + */ + overflowWidgetsDomNode?: HTMLElement; + } + /** * A rich code editor. */ diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 6ff3c4e2523..6da8d810814 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1616,7 +1616,10 @@ declare module 'vscode' { export namespace notebook { export function registerNotebookContentProvider( notebookType: string, - provider: NotebookContentProvider + provider: NotebookContentProvider, + options?: { + transientMetadata?: { [K in keyof NotebookCellMetadata]?: boolean } + } ): Disposable; export function registerNotebookKernelProvider( diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index 5d5f3d43fe8..2d5a9a1e840 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -339,7 +339,9 @@ abstract class AbstractCellRenderer extends Disposable { const modifiedMetadataSource = this._getFormatedMetadataJSON(this.cell.modified?.metadata || {}, this.cell.modified?.language); if (originalMetadataSource !== modifiedMetadataSource) { this._metadataEditor = this.instantiationService.createInstance(DiffEditorWidget, this._metadataEditorContainer!, { - ...fixedDiffEditorOptions + ...fixedDiffEditorOptions, + overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode() + }); DOM.addClass(this._metadataEditorContainer!, 'diff'); @@ -371,7 +373,8 @@ abstract class AbstractCellRenderer extends Disposable { dimension: { width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true), height: 0 - } + }, + overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode() }, {}); const mode = this.modeService.create('json'); @@ -408,7 +411,8 @@ abstract class AbstractCellRenderer extends Disposable { const modifiedOutputsSource = this._getFormatedOutputJSON(this.cell.modified?.outputs || []); if (originalOutputsSource !== modifiedOutputsSource) { this._outputEditor = this.instantiationService.createInstance(DiffEditorWidget, this._outputEditorContainer!, { - ...fixedDiffEditorOptions + ...fixedDiffEditorOptions, + overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode() }); DOM.addClass(this._outputEditorContainer!, 'diff'); @@ -440,7 +444,8 @@ abstract class AbstractCellRenderer extends Disposable { dimension: { width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true), height: 0 - } + }, + overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode() }, {}); const mode = this.modeService.create('json'); @@ -514,7 +519,8 @@ export class UnchangedCell extends AbstractCellRenderer { dimension: { width: this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN, height: editorHeight - } + }, + overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode() }, {}); this._register(this._editor.onDidContentSizeChange((e) => { @@ -601,7 +607,8 @@ export class DeletedCell extends AbstractCellRenderer { dimension: { width: (this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN) / 2 - 18, height: editorHeight - } + }, + overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode() }, {}); this._layoutInfo.editorHeight = editorHeight; @@ -686,7 +693,8 @@ export class InsertCell extends AbstractCellRenderer { dimension: { width: (this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN) / 2 - 18, height: editorHeight - } + }, + overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode() }, {}); this._layoutInfo.editorHeight = editorHeight; @@ -768,7 +776,8 @@ export class ModifiedCell extends AbstractCellRenderer { this._editorContainer = DOM.append(sourceContainer, DOM.$('.editor-container')); this._editor = this.instantiationService.createInstance(DiffEditorWidget, this._editorContainer, { - ...fixedDiffEditorOptions + ...fixedDiffEditorOptions, + overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode() }); DOM.addClass(this._editorContainer, 'diff'); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/common.ts b/src/vs/workbench/contrib/notebook/browser/diff/common.ts index f1df35485c8..7bc82bd4f9e 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/common.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/common.ts @@ -11,6 +11,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; export interface INotebookTextDiffEditor { onMouseUp: Event<{ readonly event: MouseEvent; readonly target: CellDiffViewModel; }>; + getOverflowContainerDomNode(): HTMLElement; getLayoutInfo(): NotebookLayoutInfo; layoutNotebookCell(cell: CellDiffViewModel, height: number): void; } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index 0e1aee2630d..45413b3711b 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -17,7 +17,7 @@ import { WorkbenchList } from 'vs/platform/list/browser/listService'; import { CellDiffViewModel } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { CellDiffRenderer, NotebookCellTextDiffListDelegate, NotebookTextDiffList } from 'vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList'; -import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { diffDiagonalFill, diffInserted, diffRemoved, editorBackground, focusBorder, foreground } from 'vs/platform/theme/common/colorRegistry'; import { INotebookEditorWorkerService } from 'vs/workbench/contrib/notebook/common/services/notebookWorkerService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -37,6 +37,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD static readonly ID: string = 'workbench.editor.notebookTextDiffEditor'; private _rootElement!: HTMLElement; + private _overflowContainer!: HTMLElement; private _dimension: DOM.Dimension | null = null; private _list!: WorkbenchList; private _fontInfo: BareFontInfo | undefined; @@ -45,7 +46,6 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD public readonly onMouseUp = this._onMouseUp.event; private _eventDispatcher: NotebookDiffEditorEventDispatcher | undefined; protected _scopeContextKeyService!: IContextKeyService; - private _inNotebookTextDiffEditor: IContextKey | null = null; constructor( @IInstantiationService readonly instantiationService: IInstantiationService, @@ -64,9 +64,10 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD protected createEditor(parent: HTMLElement): void { this._rootElement = DOM.append(parent, DOM.$('.notebook-text-diff-editor')); - // this._scopeContextKeyService = this._register(this.contextKeyService.createScoped(parent)); - this._inNotebookTextDiffEditor = IN_NOTEBOOK_TEXT_DIFF_EDITOR.bindTo(this.contextKeyService); - this._inNotebookTextDiffEditor.set(true); + this._overflowContainer = document.createElement('div'); + DOM.addClass(this._overflowContainer, 'notebook-overflow-widget-container'); + DOM.addClass(this._overflowContainer, 'monaco-editor'); + DOM.append(parent, this._overflowContainer); const renderer = this.instantiationService.createInstance(CellDiffRenderer, this); @@ -262,6 +263,10 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD return this._rootElement; } + getOverflowContainerDomNode(): HTMLElement { + return this._overflowContainer; + } + getControl(): NotebookEditorWidget | undefined { return undefined; } From 7c986e94f4c3384ca166b535da25b360c70abd61 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 25 Aug 2020 21:25:40 -0700 Subject: [PATCH 562/736] transient metadata --- .../api/browser/mainThreadNotebook.ts | 9 +- .../workbench/api/common/extHost.api.impl.ts | 6 +- .../workbench/api/common/extHost.protocol.ts | 4 +- .../workbench/api/common/extHostNotebook.ts | 5 +- .../notebook/browser/contrib/coreActions.ts | 10 +- .../browser/view/renderers/cellRenderer.ts | 4 +- .../contrib/notebook/common/model/cellEdit.ts | 32 +++++ .../common/model/notebookTextModel.ts | 110 +++++++++++++++--- .../contrib/notebook/common/notebookCommon.ts | 2 + .../notebook/common/notebookService.ts | 3 +- 10 files changed, 152 insertions(+), 33 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index a1acb30628d..0abd71a96ad 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -9,7 +9,7 @@ import { MainContext, MainThreadNotebookShape, NotebookExtensionDescription, IEx import { Disposable, IDisposable, combinedDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import { INotebookService, IMainNotebookController } from 'vs/workbench/contrib/notebook/common/notebookService'; -import { NOTEBOOK_DISPLAY_ORDER, NotebookCellOutputsSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, INotebookKernelInfo, INotebookKernelInfoDto, IEditor, INotebookDocumentFilter, DisplayOrderKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NOTEBOOK_DISPLAY_ORDER, NotebookCellOutputsSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, INotebookKernelInfo, INotebookKernelInfoDto, IEditor, INotebookDocumentFilter, DisplayOrderKey, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -375,10 +375,11 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo // } } - async $registerNotebookProvider(_extension: NotebookExtensionDescription, _viewType: string, _supportBackup: boolean, _kernel: INotebookKernelInfoDto | undefined): Promise { + async $registerNotebookProvider(_extension: NotebookExtensionDescription, _viewType: string, _supportBackup: boolean, _kernel: INotebookKernelInfoDto | undefined, options: { transientMetadata: TransientMetadata }): Promise { const controller: IMainNotebookController = { kernel: _kernel, supportBackup: _supportBackup, + options: options, reloadNotebook: async (mainthreadTextModel: NotebookTextModel) => { const data = await this._proxy.$resolveNotebookData(_viewType, mainthreadTextModel.uri); if (!data) { @@ -387,6 +388,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo mainthreadTextModel.languages = data.languages; mainthreadTextModel.metadata = data.metadata; + mainthreadTextModel.transientMetadata = options.transientMetadata; const edits: ICellEditOperation[] = [ { editType: CellEditType.Delete, count: mainthreadTextModel.cells.length, index: 0 }, @@ -410,6 +412,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo textModel.languages = data.languages; textModel.metadata = data.metadata; + textModel.transientMetadata = options.transientMetadata; if (data.cells.length) { textModel.initialize(data!.cells); @@ -551,7 +554,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo async $updateNotebookCellMetadata(viewType: string, resource: UriComponents, handle: number, metadata: NotebookCellMetadata): Promise { this.logService.debug('MainThreadNotebooks#updateNotebookCellMetadata', resource.path, handle, metadata); const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); - textModel?.updateNotebookCellMetadata(handle, metadata); + textModel?.changeCellMetadata(handle, metadata, true); } async $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[]): Promise { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index cbf9308517b..f6810e625a4 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -950,9 +950,11 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension); return extHostNotebook.onDidChangeActiveNotebookKernel; }, - registerNotebookContentProvider: (viewType: string, provider: vscode.NotebookContentProvider) => { + registerNotebookContentProvider: (viewType: string, provider: vscode.NotebookContentProvider, options?: { + transientMetadata?: { [K in keyof vscode.NotebookCellMetadata]?: boolean } + }) => { checkProposedApiEnabled(extension); - return extHostNotebook.registerNotebookContentProvider(extension, viewType, provider); + return extHostNotebook.registerNotebookContentProvider(extension, viewType, provider, options); }, registerNotebookKernel: (id: string, selector: vscode.GlobPattern[], kernel: vscode.NotebookKernel) => { checkProposedApiEnabled(extension); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index e0415cf4584..188c1a88dd8 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -51,7 +51,7 @@ import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; import { TunnelOptions } from 'vs/platform/remote/common/tunnel'; import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline'; import { revive } from 'vs/base/common/marshalling'; -import { IProcessedOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEvent, NotebookDataDto, INotebookKernelInfoDto, IMainCellDto, INotebookDocumentFilter, INotebookKernelInfoDto2 } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { IProcessedOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEvent, NotebookDataDto, INotebookKernelInfoDto, IMainCellDto, INotebookDocumentFilter, INotebookKernelInfoDto2, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; import { Dto } from 'vs/base/common/types'; import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; @@ -715,7 +715,7 @@ export type NotebookCellOutputsSplice = [ ]; export interface MainThreadNotebookShape extends IDisposable { - $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, supportBackup: boolean, kernelInfoDto: INotebookKernelInfoDto | undefined): Promise; + $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, supportBackup: boolean, kernelInfoDto: INotebookKernelInfoDto | undefined, options: { transientMetadata: TransientMetadata }): Promise; $onNotebookChange(viewType: string, resource: UriComponents): Promise; $unregisterNotebookProvider(viewType: string): Promise; $registerNotebookKernel(extension: NotebookExtensionDescription, id: string, label: string, selectors: (string | IRelativePattern)[], preloads: UriComponents[]): Promise; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 240d9a4fb6d..e6429ff35ae 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -954,6 +954,9 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN extension: IExtensionDescription, viewType: string, provider: vscode.NotebookContentProvider & { kernel?: vscode.NotebookKernel }, + options?: { + transientMetadata?: { [K in keyof NotebookCellMetadata]?: boolean } + } ): vscode.Disposable { if (this._notebookContentProviders.has(viewType)) { @@ -985,7 +988,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN const supportBackup = !!provider.backupNotebook; - this._proxy.$registerNotebookProvider({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, viewType, supportBackup, provider.kernel ? { id: viewType, label: provider.kernel.label, extensionLocation: extension.extensionLocation, preloads: provider.kernel.preloads } : undefined); + this._proxy.$registerNotebookProvider({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, viewType, supportBackup, provider.kernel ? { id: viewType, label: provider.kernel.label, extensionLocation: extension.extensionLocation, preloads: provider.kernel.preloads } : undefined, { transientMetadata: options?.transientMetadata || {} }); return new extHostTypes.Disposable(() => { listener.dispose(); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 7c41f1bf8c3..55419ec854e 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -1292,7 +1292,7 @@ registerAction2(class extends NotebookCellAction { editor.viewModel.notebookDocument.clearCellOutput(context.cell.handle); if (context.cell.metadata && context.cell.metadata?.runState !== NotebookCellRunState.Running) { - context.notebookEditor.viewModel!.notebookDocument.changeCellMetadata(context.cell.handle, { + context.notebookEditor.viewModel!.notebookDocument.deltaCellMetadata(context.cell.handle, { runState: NotebookCellRunState.Idle, runStartTime: undefined, lastRunDuration: undefined, @@ -1562,7 +1562,7 @@ registerAction2(class extends NotebookCellAction { } async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { - context.notebookEditor.viewModel!.notebookDocument.changeCellMetadata(context.cell.handle, { inputCollapsed: true }); + context.notebookEditor.viewModel!.notebookDocument.deltaCellMetadata(context.cell.handle, { inputCollapsed: true }); } }); @@ -1585,7 +1585,7 @@ registerAction2(class extends NotebookCellAction { } async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { - context.notebookEditor.viewModel!.notebookDocument.changeCellMetadata(context.cell.handle, { inputCollapsed: false }); + context.notebookEditor.viewModel!.notebookDocument.deltaCellMetadata(context.cell.handle, { inputCollapsed: false }); } }); @@ -1608,7 +1608,7 @@ registerAction2(class extends NotebookCellAction { } async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { - context.notebookEditor.viewModel!.notebookDocument.changeCellMetadata(context.cell.handle, { outputCollapsed: true }); + context.notebookEditor.viewModel!.notebookDocument.deltaCellMetadata(context.cell.handle, { outputCollapsed: true }); } }); @@ -1631,7 +1631,7 @@ registerAction2(class extends NotebookCellAction { } async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { - context.notebookEditor.viewModel!.notebookDocument.changeCellMetadata(context.cell.handle, { outputCollapsed: false }); + context.notebookEditor.viewModel!.notebookDocument.deltaCellMetadata(context.cell.handle, { outputCollapsed: false }); } }); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 9d02f255038..19a715474a2 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -329,9 +329,9 @@ abstract class AbstractCellRenderer { } if (templateData.currentRenderedCell.metadata?.inputCollapsed) { - this.notebookEditor.viewModel!.notebookDocument.changeCellMetadata(templateData.currentRenderedCell.handle, { inputCollapsed: false }); + this.notebookEditor.viewModel!.notebookDocument.deltaCellMetadata(templateData.currentRenderedCell.handle, { inputCollapsed: false }); } else if (templateData.currentRenderedCell.metadata?.outputCollapsed) { - this.notebookEditor.viewModel!.notebookDocument.changeCellMetadata(templateData.currentRenderedCell.handle, { outputCollapsed: false }); + this.notebookEditor.viewModel!.notebookDocument.deltaCellMetadata(templateData.currentRenderedCell.handle, { outputCollapsed: false }); } })); } diff --git a/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts b/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts index aaf080d20d1..abd87ce1d2c 100644 --- a/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts +++ b/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts @@ -6,6 +6,7 @@ import { IResourceUndoRedoElement, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo'; import { URI } from 'vs/base/common/uri'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; +import { NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; /** * It should not modify Undo/Redo stack @@ -14,6 +15,7 @@ export interface ITextCellEditingDelegate { insertCell?(index: number, cell: NotebookCellTextModel): void; deleteCell?(index: number): void; moveCell?(fromIndex: number, length: number, toIndex: number, beforeSelections: number[] | undefined, endSelections: number[] | undefined): void; + updateCellMetadata?(index: number, newMetadata: NotebookCellMetadata | undefined): void; emitSelections(selections: number[]): void; } @@ -183,3 +185,33 @@ export class SpliceCellsEdit implements IResourceUndoRedoElement { } } } + +export class CellMetadataEdit implements IResourceUndoRedoElement { + type: UndoRedoElementType.Resource = UndoRedoElementType.Resource; + label: string = 'Update Cell Metadata'; + constructor( + public resource: URI, + readonly index: number, + readonly oldMetadata: NotebookCellMetadata | undefined, + readonly newMetadata: NotebookCellMetadata | undefined, + private editingDelegate: ITextCellEditingDelegate, + ) { + + } + + undo(): void { + if (!this.editingDelegate.updateCellMetadata) { + return; + } + + this.editingDelegate.updateCellMetadata(this.index, this.oldMetadata); + } + + redo(): void | Promise { + if (!this.editingDelegate.updateCellMetadata) { + return; + } + + this.editingDelegate.updateCellMetadata(this.index, this.newMetadata); + } +} diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index fe268309fb9..79903890afc 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -8,10 +8,10 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { INotebookTextModel, NotebookCellOutputsSplice, NotebookCellTextModelSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, NotebookCellsChangedEvent, CellKind, IProcessedOutput, notebookDocumentMetadataDefaults, diff, NotebookCellsChangeType, ICellDto2, IMainCellDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookTextModel, NotebookCellOutputsSplice, NotebookCellTextModelSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, NotebookCellsChangedEvent, CellKind, IProcessedOutput, notebookDocumentMetadataDefaults, diff, NotebookCellsChangeType, ICellDto2, IMainCellDto, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ITextSnapshot } from 'vs/editor/common/model'; import { IUndoRedoService, UndoRedoElementType, IUndoRedoElement, IResourceUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo'; -import { InsertCellEdit, DeleteCellEdit, MoveCellEdit, SpliceCellsEdit } from 'vs/workbench/contrib/notebook/common/model/cellEdit'; +import { InsertCellEdit, DeleteCellEdit, MoveCellEdit, SpliceCellsEdit, CellMetadataEdit } from 'vs/workbench/contrib/notebook/common/model/cellEdit'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; export class NotebookTextModelSnapshot implements ITextSnapshot { @@ -128,6 +128,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel cells: NotebookCellTextModel[]; languages: string[] = []; metadata: NotebookDocumentMetadata = notebookDocumentMetadataDefaults; + transientMetadata: TransientMetadata = {}; private _isUntitled: boolean | undefined = undefined; private _versionId = 0; @@ -259,7 +260,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel break; case CellEditType.Metadata: this.assertIndex(edit.index); - this.changeCellMetadata(this.cells[edit.index].handle, edit.metadata); + this.deltaCellMetadata(this.cells[edit.index].handle, edit.metadata); break; } } @@ -342,14 +343,6 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._onDidChangeMetadata.fire(this.metadata); } - updateNotebookCellMetadata(handle: number, metadata: NotebookCellMetadata) { - const cell = this.cells.find(cell => cell.handle === handle); - - if (cell) { - cell.metadata = metadata; - } - } - insertTemplateCell(cell: NotebookCellTextModel) { if (this.cells.length > 0 || this._isUntitled !== undefined) { return; @@ -504,16 +497,99 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel } } - changeCellMetadata(handle: number, newMetadata: NotebookCellMetadata) { + private _compareCellMetadata(a: NotebookCellMetadata | undefined, b: NotebookCellMetadata | undefined) { + if (a?.editable !== b?.editable && !this.transientMetadata.editable) { + return true; + } + + if (a?.runnable !== b?.runnable && !this.transientMetadata.runnable) { + return true; + } + + if (a?.breakpointMargin !== b?.breakpointMargin && !this.transientMetadata.breakpointMargin) { + return true; + } + + if (a?.hasExecutionOrder !== b?.hasExecutionOrder && !this.transientMetadata.hasExecutionOrder) { + return true; + } + + if (a?.executionOrder !== b?.executionOrder && !this.transientMetadata.executionOrder) { + return true; + } + + if (a?.statusMessage !== b?.statusMessage && !this.transientMetadata.statusMessage) { + return true; + } + + if (a?.runState !== b?.runState && !this.transientMetadata.runState) { + return true; + } + + if (a?.runStartTime !== b?.runStartTime && !this.transientMetadata.runStartTime) { + return true; + } + + if (a?.lastRunDuration !== b?.lastRunDuration && !this.transientMetadata.lastRunDuration) { + return true; + } + + if (a?.inputCollapsed !== b?.inputCollapsed && !this.transientMetadata.inputCollapsed) { + return true; + } + + if (a?.outputCollapsed !== b?.outputCollapsed && !this.transientMetadata.outputCollapsed) { + return true; + } + + if (a?.custom !== b?.custom && !this.transientMetadata.custom) { + return true; + } + + return false; + } + + changeCellMetadata(handle: number, metadata: NotebookCellMetadata | undefined, pushUndoStop: boolean) { + const cell = this.cells.find(cell => cell.handle === handle); + + if (!cell) { + return; + } + + const triggerDirtyChange = this._compareCellMetadata(cell.metadata, metadata); + + if (triggerDirtyChange) { + if (pushUndoStop) { + const index = this.cells.indexOf(cell); + this._operationManager.pushEditOperation(new CellMetadataEdit(this.uri, index, Object.freeze(cell.metadata), Object.freeze(metadata), { + updateCellMetadata: (index, newMetadata) => { + const cell = this.cells[index]; + if (!cell) { + return; + } + this.changeCellMetadata(cell.handle, newMetadata, false); + }, + emitSelections: this._emitSelectionsDelegate.bind(this) + })); + } + cell.metadata = metadata; + this.setDirty(true); + this._onDidChangeContent.fire(); + } else { + cell.metadata = metadata; + } + + this._increaseVersionId(); + this._onDidModelChangeProxy.fire({ kind: NotebookCellsChangeType.ChangeMetadata, versionId: this._versionId, index: this.cells.indexOf(cell), metadata: cell.metadata }); + } + + deltaCellMetadata(handle: number, newMetadata: NotebookCellMetadata) { const cell = this._mapping.get(handle); if (cell) { - cell.metadata = { + this.changeCellMetadata(handle, { ...cell.metadata, ...newMetadata - }; - - this._increaseVersionId(); - this._onDidModelChangeProxy.fire({ kind: NotebookCellsChangeType.ChangeMetadata, versionId: this._versionId, index: this.cells.indexOf(cell), metadata: cell.metadata }); + }, true); } } diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 52520303aa6..f4327ddd295 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -103,6 +103,8 @@ export interface NotebookCellMetadata { custom?: { [key: string]: unknown }; } +export type TransientMetadata = { [K in keyof NotebookCellMetadata]?: boolean }; + export interface INotebookDisplayOrder { defaultOrder: string[]; userOrder?: string[]; diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index 1eb32840394..89e0161170e 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -10,7 +10,7 @@ import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.pr import { Event } from 'vs/base/common/event'; import { INotebookTextModel, INotebookRendererInfo, INotebookKernelInfo, INotebookKernelInfoDto, - IEditor, ICellEditOperation, NotebookCellOutputsSplice, INotebookKernelProvider, INotebookKernelInfo2 + IEditor, ICellEditOperation, NotebookCellOutputsSplice, INotebookKernelProvider, INotebookKernelInfo2, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -24,6 +24,7 @@ export const INotebookService = createDecorator('notebookServi export interface IMainNotebookController { kernel: INotebookKernelInfoDto | undefined; supportBackup: boolean; + options: { transientMetadata: TransientMetadata }; createNotebook(textModel: NotebookTextModel, editorId?: string, backupId?: string): Promise; reloadNotebook(mainthreadTextModel: NotebookTextModel): Promise; resolveNotebookEditor(viewType: string, uri: URI, editorId: string): Promise; From e88c040df7d5a906ec1283a0e5299aab90016404 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 26 Aug 2020 10:14:47 -0700 Subject: [PATCH 563/736] ignore transient metadata in diff editor. --- .../workbench/api/common/extHostNotebook.ts | 4 +- .../notebook/browser/diff/cellComponents.ts | 21 ++++++- .../contrib/notebook/browser/diff/common.ts | 2 + .../browser/diff/notebookTextDiffEditor.ts | 15 +++-- .../browser/notebookDiffEditorInput.ts | 8 --- .../contrib/notebook/common/model/cellEdit.ts | 6 +- .../common/model/notebookCellTextModel.ts | 6 +- .../common/model/notebookTextModel.ts | 61 ++++--------------- .../contrib/notebook/common/notebookCommon.ts | 2 +- .../notebookEditorModelResolverService.ts | 6 ++ 10 files changed, 57 insertions(+), 74 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index e6429ff35ae..9c639c41567 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -428,9 +428,9 @@ export class ExtHostNotebookDocument extends Disposable { this._emitter.emitCellLanguageChange(event); } - private _changeCellMetadata(index: number, newMetadata: NotebookCellMetadata): void { + private _changeCellMetadata(index: number, newMetadata: NotebookCellMetadata | undefined): void { const cell = this._cells[index]; - cell.setMetadata(newMetadata); + cell.setMetadata(newMetadata || {}); const event: vscode.NotebookCellMetadataChangeEvent = { document: this.notebookDocument, cell: cell.cell }; this._emitter.emitCellMetadataChange(event); } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index 2d5a9a1e840..4bc5911f51b 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -235,7 +235,7 @@ abstract class AbstractCellRenderer extends Disposable { { updateInfoRendering: this.updateMetadataRendering.bind(this), checkIfModified: (cell) => { - return cell.type === 'modified' && hash(cell.original?.metadata ?? {}) !== hash(cell.modified?.metadata ?? {}); + return cell.type === 'modified' && hash(this._getFormatedMetadataJSON(cell.original?.metadata || {})) !== hash(this._getFormatedMetadataJSON(cell.modified?.metadata ?? {})); }, getFoldingState: (cell) => { return cell.metadataFoldingState; @@ -321,10 +321,25 @@ abstract class AbstractCellRenderer extends Disposable { } } - private _getFormatedMetadataJSON(metadata: NotebookCellMetadata, language?: string) { + protected _getFormatedMetadataJSON(metadata: NotebookCellMetadata, language?: string) { + let filteredMetadata: { [key: string]: any } = {}; + if (this.notebookEditor.textModel) { + const transientMetadata = this.notebookEditor.textModel!.transientMetadata; + + const keys = new Set([...Object.keys(metadata)]); + for (let key of keys) { + if (!(transientMetadata[key as keyof NotebookCellMetadata]) + ) { + filteredMetadata[key] = metadata[key as keyof NotebookCellMetadata]; + } + } + } else { + filteredMetadata = metadata; + } + const content = JSON.stringify({ language, - ...metadata + ...filteredMetadata }); const edits = format(content, undefined, {}); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/common.ts b/src/vs/workbench/contrib/notebook/browser/diff/common.ts index 7bc82bd4f9e..505718cea7c 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/common.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/common.ts @@ -8,8 +8,10 @@ import { CellDiffViewModel } from 'vs/workbench/contrib/notebook/browser/diff/ce import { Event } from 'vs/base/common/event'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; export interface INotebookTextDiffEditor { + readonly textModel?: NotebookTextModel; onMouseUp: Event<{ readonly event: MouseEvent; readonly target: CellDiffViewModel; }>; getOverflowContainerDomNode(): HTMLElement; getLayoutInfo(): NotebookLayoutInfo; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index 45413b3711b..de6076b0ff3 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -30,6 +30,7 @@ import { Emitter } from 'vs/base/common/event'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { NotebookDiffEditorEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; +import { INotebookDiffEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; export const IN_NOTEBOOK_TEXT_DIFF_EDITOR = new RawContextKey('isInNotebookTextDiffEditor', false); @@ -46,6 +47,10 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD public readonly onMouseUp = this._onMouseUp.event; private _eventDispatcher: NotebookDiffEditorEventDispatcher | undefined; protected _scopeContextKeyService!: IContextKeyService; + private _model: INotebookDiffEditorModel | null = null; + get textModel() { + return this._model?.modified.notebook; + } constructor( @IInstantiationService readonly instantiationService: IInstantiationService, @@ -132,19 +137,19 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD async setInput(input: NotebookDiffEditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { await super.setInput(input, options, context, token); - const model = await input.resolve(); - if (model === null) { + this._model = await input.resolve(); + if (this._model === null) { return; } this._eventDispatcher = new NotebookDiffEditorEventDispatcher(); - const diffResult = await this.notebookEditorWorkerService.computeDiff(model.original.resource, model.modified.resource); + const diffResult = await this.notebookEditorWorkerService.computeDiff(this._model.original.resource, this._model.modified.resource); const cellChanges = diffResult.cellsDiff.changes; const cellDiffViewModels: CellDiffViewModel[] = []; - const originalModel = model.original.notebook; - const modifiedModel = model.modified.notebook; + const originalModel = this._model.original.notebook; + const modifiedModel = this._model.modified.notebook; let originalCellIndex = 0; let modifiedCellIndex = 0; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts index efb4c9a703f..be382dcd70d 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts @@ -198,14 +198,6 @@ ${patterns} if (!this._textModel) { this._textModel = await this._notebookModelResolverService.resolve(this.resource, this.viewType!, editorId); this._originalTextModel = await this._notebookModelResolverService.resolve(this.originalResource, this.viewType!, editorId); - - // this._register(this._textModel.object.onDidChangeDirty(() => { - // this._onDidChangeDirty.fire(); - // })); - - // if (this._textModel.object.isDirty()) { - // this._onDidChangeDirty.fire(); - // } } return new NotebookDiffEditorModel(this._originalTextModel!.object as NotebookEditorModel, this._textModel.object as NotebookEditorModel); diff --git a/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts b/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts index abd87ce1d2c..5cde6b3bfaa 100644 --- a/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts +++ b/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts @@ -15,7 +15,7 @@ export interface ITextCellEditingDelegate { insertCell?(index: number, cell: NotebookCellTextModel): void; deleteCell?(index: number): void; moveCell?(fromIndex: number, length: number, toIndex: number, beforeSelections: number[] | undefined, endSelections: number[] | undefined): void; - updateCellMetadata?(index: number, newMetadata: NotebookCellMetadata | undefined): void; + updateCellMetadata?(index: number, newMetadata: NotebookCellMetadata): void; emitSelections(selections: number[]): void; } @@ -192,8 +192,8 @@ export class CellMetadataEdit implements IResourceUndoRedoElement { constructor( public resource: URI, readonly index: number, - readonly oldMetadata: NotebookCellMetadata | undefined, - readonly newMetadata: NotebookCellMetadata | undefined, + readonly oldMetadata: NotebookCellMetadata, + readonly newMetadata: NotebookCellMetadata, private editingDelegate: ITextCellEditingDelegate, ) { diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts index 2e85398d330..6d4b29511e2 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts @@ -32,13 +32,13 @@ export class NotebookCellTextModel extends Disposable implements ICell { return this._outputs; } - private _metadata: NotebookCellMetadata | undefined; + private _metadata: NotebookCellMetadata; get metadata() { return this._metadata; } - set metadata(newMetadata: NotebookCellMetadata | undefined) { + set metadata(newMetadata: NotebookCellMetadata) { this._metadata = newMetadata; this._hash = null; this._onDidChangeMetadata.fire(); @@ -89,7 +89,7 @@ export class NotebookCellTextModel extends Disposable implements ICell { ) { super(); this._outputs = outputs; - this._metadata = metadata; + this._metadata = metadata || {}; } getValue(): string { diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 79903890afc..3dc85af8047 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -187,7 +187,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel ) { const cellHandle = this._cellhandlePool++; const cellUri = CellUri.generate(this.uri, cellHandle); - return new NotebookCellTextModel(cellUri, cellHandle, source, language, cellKind, outputs || [], metadata, this._modelService); + return new NotebookCellTextModel(cellUri, cellHandle, source, language, cellKind, outputs || [], metadata || {}, this._modelService); } initialize(cells: ICellDto2[]) { @@ -497,59 +497,22 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel } } - private _compareCellMetadata(a: NotebookCellMetadata | undefined, b: NotebookCellMetadata | undefined) { - if (a?.editable !== b?.editable && !this.transientMetadata.editable) { - return true; - } - - if (a?.runnable !== b?.runnable && !this.transientMetadata.runnable) { - return true; - } - - if (a?.breakpointMargin !== b?.breakpointMargin && !this.transientMetadata.breakpointMargin) { - return true; - } - - if (a?.hasExecutionOrder !== b?.hasExecutionOrder && !this.transientMetadata.hasExecutionOrder) { - return true; - } - - if (a?.executionOrder !== b?.executionOrder && !this.transientMetadata.executionOrder) { - return true; - } - - if (a?.statusMessage !== b?.statusMessage && !this.transientMetadata.statusMessage) { - return true; - } - - if (a?.runState !== b?.runState && !this.transientMetadata.runState) { - return true; - } - - if (a?.runStartTime !== b?.runStartTime && !this.transientMetadata.runStartTime) { - return true; - } - - if (a?.lastRunDuration !== b?.lastRunDuration && !this.transientMetadata.lastRunDuration) { - return true; - } - - if (a?.inputCollapsed !== b?.inputCollapsed && !this.transientMetadata.inputCollapsed) { - return true; - } - - if (a?.outputCollapsed !== b?.outputCollapsed && !this.transientMetadata.outputCollapsed) { - return true; - } - - if (a?.custom !== b?.custom && !this.transientMetadata.custom) { - return true; + private _compareCellMetadata(a: NotebookCellMetadata, b: NotebookCellMetadata) { + const keys = new Set([...Object.keys(a || {}), ...Object.keys(b || {})]); + for (let key of keys) { + if ( + (a[key as keyof NotebookCellMetadata] !== b[key as keyof NotebookCellMetadata]) + && + !(this.transientMetadata[key as keyof NotebookCellMetadata]) + ) { + return true; + } } return false; } - changeCellMetadata(handle: number, metadata: NotebookCellMetadata | undefined, pushUndoStop: boolean) { + changeCellMetadata(handle: number, metadata: NotebookCellMetadata, pushUndoStop: boolean) { const cell = this.cells.find(cell => cell.handle === handle); if (!cell) { diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index f4327ddd295..b634b6bd63e 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -397,7 +397,7 @@ export interface NotebookCellsChangeMetadataEvent { readonly kind: NotebookCellsChangeType.ChangeMetadata; readonly versionId: number; readonly index: number; - readonly metadata: NotebookCellMetadata; + readonly metadata: NotebookCellMetadata | undefined; } export type NotebookCellsChangedEvent = NotebookCellsInitializeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookCellClearOutputEvent | NotebookCellsClearOutputEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent; diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts index 139c02ae5b4..11587e61242 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts @@ -76,3 +76,9 @@ export class NotebookModelResolverService implements INotebookEditorModelResolve }; } } + +// notebookService.onDidAddDocument + +// resolve() + +// notebookService.onDidRemoveDocument ... From 32f29cfdca93433e9bec3bd4c00eaffcaf8a14d7 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 26 Aug 2020 10:51:46 -0700 Subject: [PATCH 564/736] transient outputs. --- src/vs/vscode.proposed.d.ts | 11 +++++++- .../api/browser/mainThreadNotebook.ts | 6 ++--- .../workbench/api/common/extHost.api.impl.ts | 3 ++- .../workbench/api/common/extHost.protocol.ts | 2 +- .../workbench/api/common/extHostNotebook.ts | 5 ++-- .../notebook/browser/diff/cellComponents.ts | 14 +++++----- .../common/model/notebookCellTextModel.ts | 21 ++++++++++++--- .../common/model/notebookTextModel.ts | 26 +++++++++++++------ .../contrib/notebook/common/notebookCommon.ts | 5 ++++ .../notebook/common/notebookService.ts | 2 +- .../notebook/test/testNotebookEditor.ts | 2 +- 11 files changed, 70 insertions(+), 27 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 6da8d810814..ebaa7d6cf51 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1618,7 +1618,16 @@ declare module 'vscode' { notebookType: string, provider: NotebookContentProvider, options?: { - transientMetadata?: { [K in keyof NotebookCellMetadata]?: boolean } + /** + * Controls if outputs change will trigger notebook document content change and if it will be used in the diff editor + * Default to false. If the content provider doesn't persisit the outputs in the file document, this should be set to true. + */ + transientOutputs: boolean; + /** + * Controls if a meetadata property change will trigger notebook document content change and if it will be used in the diff editor + * Default to false. If the content provider doesn't persisit a metadata property in the file document, it should be set to true. + */ + transientMetadata: { [K in keyof NotebookCellMetadata]?: boolean } } ): Disposable; diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 0abd71a96ad..a377146ad2b 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -375,7 +375,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo // } } - async $registerNotebookProvider(_extension: NotebookExtensionDescription, _viewType: string, _supportBackup: boolean, _kernel: INotebookKernelInfoDto | undefined, options: { transientMetadata: TransientMetadata }): Promise { + async $registerNotebookProvider(_extension: NotebookExtensionDescription, _viewType: string, _supportBackup: boolean, _kernel: INotebookKernelInfoDto | undefined, options: { transientOutputs: boolean; transientMetadata: TransientMetadata }): Promise { const controller: IMainNotebookController = { kernel: _kernel, supportBackup: _supportBackup, @@ -388,7 +388,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo mainthreadTextModel.languages = data.languages; mainthreadTextModel.metadata = data.metadata; - mainthreadTextModel.transientMetadata = options.transientMetadata; + mainthreadTextModel.transientOptions = options; const edits: ICellEditOperation[] = [ { editType: CellEditType.Delete, count: mainthreadTextModel.cells.length, index: 0 }, @@ -412,7 +412,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo textModel.languages = data.languages; textModel.metadata = data.metadata; - textModel.transientMetadata = options.transientMetadata; + textModel.transientOptions = options; if (data.cells.length) { textModel.initialize(data!.cells); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index f6810e625a4..ea6cfe93bfe 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -951,7 +951,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostNotebook.onDidChangeActiveNotebookKernel; }, registerNotebookContentProvider: (viewType: string, provider: vscode.NotebookContentProvider, options?: { - transientMetadata?: { [K in keyof vscode.NotebookCellMetadata]?: boolean } + transientOutputs: boolean; + transientMetadata: { [K in keyof vscode.NotebookCellMetadata]?: boolean } }) => { checkProposedApiEnabled(extension); return extHostNotebook.registerNotebookContentProvider(extension, viewType, provider, options); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 188c1a88dd8..13c722e1926 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -715,7 +715,7 @@ export type NotebookCellOutputsSplice = [ ]; export interface MainThreadNotebookShape extends IDisposable { - $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, supportBackup: boolean, kernelInfoDto: INotebookKernelInfoDto | undefined, options: { transientMetadata: TransientMetadata }): Promise; + $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, supportBackup: boolean, kernelInfoDto: INotebookKernelInfoDto | undefined, options: { transientOutputs: boolean; transientMetadata: TransientMetadata }): Promise; $onNotebookChange(viewType: string, resource: UriComponents): Promise; $unregisterNotebookProvider(viewType: string): Promise; $registerNotebookKernel(extension: NotebookExtensionDescription, id: string, label: string, selectors: (string | IRelativePattern)[], preloads: UriComponents[]): Promise; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 9c639c41567..bd8fad9da0e 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -955,7 +955,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN viewType: string, provider: vscode.NotebookContentProvider & { kernel?: vscode.NotebookKernel }, options?: { - transientMetadata?: { [K in keyof NotebookCellMetadata]?: boolean } + transientOutputs: boolean; + transientMetadata: { [K in keyof NotebookCellMetadata]?: boolean }; } ): vscode.Disposable { @@ -988,7 +989,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN const supportBackup = !!provider.backupNotebook; - this._proxy.$registerNotebookProvider({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, viewType, supportBackup, provider.kernel ? { id: viewType, label: provider.kernel.label, extensionLocation: extension.extensionLocation, preloads: provider.kernel.preloads } : undefined, { transientMetadata: options?.transientMetadata || {} }); + this._proxy.$registerNotebookProvider({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, viewType, supportBackup, provider.kernel ? { id: viewType, label: provider.kernel.label, extensionLocation: extension.extensionLocation, preloads: provider.kernel.preloads } : undefined, { transientOutputs: options?.transientOutputs || false, transientMetadata: options?.transientMetadata || {} }); return new extHostTypes.Disposable(() => { listener.dispose(); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index 4bc5911f51b..f339970b91e 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -261,7 +261,7 @@ abstract class AbstractCellRenderer extends Disposable { { updateInfoRendering: this.updateOutputRendering.bind(this), checkIfModified: (cell) => { - return cell.type === 'modified' && hash(cell.original?.outputs ?? []) !== hash(cell.modified?.outputs ?? []); + return !this.notebookEditor.textModel!.transientOptions.transientOutputs && cell.type === 'modified' && hash(cell.original?.outputs ?? []) !== hash(cell.modified?.outputs ?? []); }, getFoldingState: (cell) => { return this.cell.outputFoldingState; @@ -324,7 +324,7 @@ abstract class AbstractCellRenderer extends Disposable { protected _getFormatedMetadataJSON(metadata: NotebookCellMetadata, language?: string) { let filteredMetadata: { [key: string]: any } = {}; if (this.notebookEditor.textModel) { - const transientMetadata = this.notebookEditor.textModel!.transientMetadata; + const transientMetadata = this.notebookEditor.textModel!.transientOptions.transientMetadata; const keys = new Set([...Object.keys(metadata)]); for (let key of keys) { @@ -421,7 +421,7 @@ abstract class AbstractCellRenderer extends Disposable { } private _buildOutputEditor() { - if (this.cell.type === 'modified') { + if (this.cell.type === 'modified' && !this.notebookEditor.textModel!.transientOptions.transientOutputs) { const originalOutputsSource = this._getFormatedOutputJSON(this.cell.original?.outputs || []); const modifiedOutputsSource = this._getFormatedOutputJSON(this.cell.modified?.outputs || []); if (originalOutputsSource !== modifiedOutputsSource) { @@ -465,9 +465,11 @@ abstract class AbstractCellRenderer extends Disposable { const mode = this.modeService.create('json'); const originaloutputSource = this._getFormatedOutputJSON( - this.cell.type === 'insert' - ? this.cell.modified!.outputs || [] - : this.cell.original!.outputs || []); + this.notebookEditor.textModel!.transientOptions + ? [] + : this.cell.type === 'insert' + ? this.cell.modified!.outputs || [] + : this.cell.original!.outputs || []); const outputModel = this.modelService.createModel(originaloutputSource, mode, undefined, true); this._outputEditor.setModel(outputModel); diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts index 6d4b29511e2..c2b34adefcf 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter, Event } from 'vs/base/common/event'; -import { ICell, IProcessedOutput, NotebookCellOutputsSplice, CellKind, NotebookCellMetadata, NotebookDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICell, IProcessedOutput, NotebookCellOutputsSplice, CellKind, NotebookCellMetadata, NotebookDocumentMetadata, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder'; import { URI } from 'vs/base/common/uri'; import * as model from 'vs/editor/common/model'; @@ -85,6 +85,7 @@ export class NotebookCellTextModel extends Disposable implements ICell { public cellKind: CellKind, outputs: IProcessedOutput[], metadata: NotebookCellMetadata | undefined, + public readonly transientOptions: TransientOptions, private readonly _modelService: ITextModelService ) { super(); @@ -108,11 +109,25 @@ export class NotebookCellTextModel extends Disposable implements ICell { } // TODO, raw outputs - this._hash = hash([hash(this.getValue()), this._metadata, this._outputs]); - // this._hash = hash(this.getValue()); + this._hash = hash([hash(this.getValue()), this._getPersisentMetadata, this.transientOptions.transientOutputs ? [] : this._outputs]); return this._hash; } + private _getPersisentMetadata() { + let filteredMetadata: { [key: string]: any } = {}; + const transientMetadata = this.transientOptions.transientMetadata; + + const keys = new Set([...Object.keys(this.metadata)]); + for (let key of keys) { + if (!(transientMetadata[key as keyof NotebookCellMetadata]) + ) { + filteredMetadata[key] = this.metadata[key as keyof NotebookCellMetadata]; + } + } + + return filteredMetadata; + } + getTextLength(): number { return this.textBuffer.getLength(); } diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 3dc85af8047..b85e95045cb 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -8,7 +8,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { INotebookTextModel, NotebookCellOutputsSplice, NotebookCellTextModelSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, NotebookCellsChangedEvent, CellKind, IProcessedOutput, notebookDocumentMetadataDefaults, diff, NotebookCellsChangeType, ICellDto2, IMainCellDto, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookTextModel, NotebookCellOutputsSplice, NotebookCellTextModelSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, NotebookCellsChangedEvent, CellKind, IProcessedOutput, notebookDocumentMetadataDefaults, diff, NotebookCellsChangeType, ICellDto2, IMainCellDto, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ITextSnapshot } from 'vs/editor/common/model'; import { IUndoRedoService, UndoRedoElementType, IUndoRedoElement, IResourceUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo'; import { InsertCellEdit, DeleteCellEdit, MoveCellEdit, SpliceCellsEdit, CellMetadataEdit } from 'vs/workbench/contrib/notebook/common/model/cellEdit'; @@ -128,7 +128,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel cells: NotebookCellTextModel[]; languages: string[] = []; metadata: NotebookDocumentMetadata = notebookDocumentMetadataDefaults; - transientMetadata: TransientMetadata = {}; + transientOptions: TransientOptions = { transientMetadata: {}, transientOutputs: false }; private _isUntitled: boolean | undefined = undefined; private _versionId = 0; @@ -187,7 +187,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel ) { const cellHandle = this._cellhandlePool++; const cellUri = CellUri.generate(this.uri, cellHandle); - return new NotebookCellTextModel(cellUri, cellHandle, source, language, cellKind, outputs || [], metadata || {}, this._modelService); + return new NotebookCellTextModel(cellUri, cellHandle, source, language, cellKind, outputs || [], metadata || {}, this.transientOptions, this._modelService); } initialize(cells: ICellDto2[]) { @@ -197,7 +197,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel const mainCells = cells.map(cell => { const cellHandle = this._cellhandlePool++; const cellUri = CellUri.generate(this.uri, cellHandle); - return new NotebookCellTextModel(cellUri, cellHandle, cell.source, cell.language, cell.cellKind, cell.outputs || [], cell.metadata, this._modelService); + return new NotebookCellTextModel(cellUri, cellHandle, cell.source, cell.language, cell.cellKind, cell.outputs || [], cell.metadata, this.transientOptions, this._modelService); }); this._isUntitled = false; @@ -245,7 +245,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel const mainCells = edit.cells.map(cell => { const cellHandle = this._cellhandlePool++; const cellUri = CellUri.generate(this.uri, cellHandle); - return new NotebookCellTextModel(cellUri, cellHandle, cell.source, cell.language, cell.cellKind, cell.outputs || [], cell.metadata, this._modelService); + return new NotebookCellTextModel(cellUri, cellHandle, cell.source, cell.language, cell.cellKind, cell.outputs || [], cell.metadata, this.transientOptions, this._modelService); }); this.insertNewCell(edit.index, mainCells, false); break; @@ -473,6 +473,12 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel spliceNotebookCellOutputs(cellHandle: number, splices: NotebookCellOutputsSplice[]): void { const cell = this._mapping.get(cellHandle); cell?.spliceNotebookCellOutputs(splices); + + if (!this.transientOptions.transientOutputs) { + this._increaseVersionId(); + this.setDirty(true); + this._onDidChangeContent.fire(); + } } clearCellOutput(handle: number) { @@ -497,13 +503,13 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel } } - private _compareCellMetadata(a: NotebookCellMetadata, b: NotebookCellMetadata) { + private _isCellMetadataChanged(a: NotebookCellMetadata, b: NotebookCellMetadata) { const keys = new Set([...Object.keys(a || {}), ...Object.keys(b || {})]); for (let key of keys) { if ( (a[key as keyof NotebookCellMetadata] !== b[key as keyof NotebookCellMetadata]) && - !(this.transientMetadata[key as keyof NotebookCellMetadata]) + !(this.transientOptions.transientMetadata[key as keyof NotebookCellMetadata]) ) { return true; } @@ -519,7 +525,11 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel return; } - const triggerDirtyChange = this._compareCellMetadata(cell.metadata, metadata); + if (!this._isCellMetadataChanged(cell.metadata, metadata)) { + return; + } + + const triggerDirtyChange = this._isCellMetadataChanged(cell.metadata, metadata); if (triggerDirtyChange) { if (pushUndoStop) { diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index b634b6bd63e..2505a5c855d 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -105,6 +105,11 @@ export interface NotebookCellMetadata { export type TransientMetadata = { [K in keyof NotebookCellMetadata]?: boolean }; +export interface TransientOptions { + transientOutputs: boolean; + transientMetadata: TransientMetadata; +} + export interface INotebookDisplayOrder { defaultOrder: string[]; userOrder?: string[]; diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index 89e0161170e..9e7a32ec7a1 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -24,7 +24,7 @@ export const INotebookService = createDecorator('notebookServi export interface IMainNotebookController { kernel: INotebookKernelInfoDto | undefined; supportBackup: boolean; - options: { transientMetadata: TransientMetadata }; + options: { transientOutputs: boolean; transientMetadata: TransientMetadata; }; createNotebook(textModel: NotebookTextModel, editorId?: string, backupId?: string): Promise; reloadNotebook(mainthreadTextModel: NotebookTextModel): Promise; resolveNotebookEditor(viewType: string, uri: URI, editorId: string): Promise; diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index 7f69f420dd2..7953687f0ce 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -45,7 +45,7 @@ export class TestCell extends NotebookCellTextModel { outputs: IProcessedOutput[], modelService: ITextModelService ) { - super(CellUri.generate(URI.parse('test:///fake/notebook'), handle), handle, source, language, cellKind, outputs, undefined, modelService); + super(CellUri.generate(URI.parse('test:///fake/notebook'), handle), handle, source, language, cellKind, outputs, undefined, { transientMetadata: {}, transientOutputs: false }, modelService); } } From 27082f3dbbb65042828c256fcb00cfbbde25c061 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 26 Aug 2020 20:17:04 +0200 Subject: [PATCH 565/736] Use an enum instead of a boolean to signal eager activation --- .../api/browser/mainThreadAuthentication.ts | 4 ++-- src/vs/workbench/api/common/extHost.protocol.ts | 4 ++-- .../api/common/extHostExtensionService.ts | 6 +++--- .../common/abstractExtensionService.ts | 17 +++++++++-------- .../extensions/common/extensionHostManager.ts | 14 +++++++------- .../services/extensions/common/extensions.ts | 17 ++++++++++++----- 6 files changed, 35 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts index e4fde65fb10..18493b01c47 100644 --- a/src/vs/workbench/api/browser/mainThreadAuthentication.ts +++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts @@ -17,7 +17,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { fromNow } from 'vs/base/common/date'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { ActivationKind, IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { Platform, platform } from 'vs/base/common/platform'; const VSO_ALLOWED_EXTENSIONS = ['github.vscode-pull-request-github', 'github.vscode-pull-request-github-insiders', 'vscode.git', 'ms-vsonline.vsonline', 'vscode.github-browser']; @@ -249,7 +249,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu } $ensureProvider(id: string): Promise { - return this.extensionService.activateByEvent(getAuthenticationProviderActivationEvent(id), true); + return this.extensionService.activateByEvent(getAuthenticationProviderActivationEvent(id), ActivationKind.Eager); } $sendDidChangeSessions(id: string, event: modes.AuthenticationSessionsChangeEvent): void { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 9f0c4e55065..5c1f6f97af2 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -42,7 +42,7 @@ import { IRevealOptions, ITreeItem } from 'vs/workbench/common/views'; import { IAdapterDescriptor, IConfig, IDebugSessionReplMode } from 'vs/workbench/contrib/debug/common/debug'; import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder'; import { ITerminalDimensions, IShellLaunchConfig, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal'; -import { ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions'; +import { ActivationKind, ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions'; import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import * as search from 'vs/workbench/services/search/common/search'; import { SaveReason } from 'vs/workbench/common/editor'; @@ -1074,7 +1074,7 @@ export type IResolveAuthorityResult = IResolveAuthorityErrorResult | IResolveAut export interface ExtHostExtensionServiceShape { $resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise; $startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise; - $activateByEvent(activationEvent: string, eager?: boolean): Promise; + $activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise; $activate(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise; $setRemoteEnvironment(env: { [key: string]: string | null; }): Promise; $updateRemoteConnectionData(connectionData: IRemoteConnectionData): Promise; diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 0653b623ba0..8d5ab4a9ec8 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -17,7 +17,7 @@ import { ExtHostConfiguration, IExtHostConfiguration } from 'vs/workbench/api/co import { ActivatedExtension, EmptyExtension, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionModule, HostExtension, ExtensionActivationTimesFragment } from 'vs/workbench/api/common/extHostExtensionActivator'; import { ExtHostStorage, IExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; -import { ExtensionActivationError, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionActivationError, checkProposedApiEnabled, ActivationKind } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import * as errors from 'vs/base/common/errors'; import type * as vscode from 'vscode'; @@ -686,8 +686,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme return this._startExtensionHost(); } - public $activateByEvent(activationEvent: string, eager: boolean = true): Promise { - if (eager) { + public $activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise { + if (activationKind === ActivationKind.Eager) { return this._activateByEvent(activationEvent, false); } diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index 2bd2b6a3282..5bd8624bc10 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -15,7 +15,7 @@ import { BetterMergeId } from 'vs/platform/extensionManagement/common/extensionM import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { ActivationTimes, ExtensionPointContribution, IExtensionService, IExtensionsStatus, IMessage, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension, IExtensionHost } from 'vs/workbench/services/extensions/common/extensions'; +import { ActivationTimes, ExtensionPointContribution, IExtensionService, IExtensionsStatus, IMessage, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension, IExtensionHost, ActivationKind } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol'; @@ -186,7 +186,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx this._startExtensionHosts(false, Array.from(this._allRequestedActivateEvents.keys())); } - public activateByEvent(activationEvent: string, eager?: boolean): Promise { + public activateByEvent(activationEvent: string, activationKind: ActivationKind = ActivationKind.Normal): Promise { if (this._installedExtensionsReady.isOpen()) { // Extensions have been scanned and interpreted @@ -198,24 +198,25 @@ export abstract class AbstractExtensionService extends Disposable implements IEx return NO_OP_VOID_PROMISE; } - return this._activateByEvent(activationEvent); + return this._activateByEvent(activationEvent, activationKind); } else { // Extensions have not been scanned yet. // Record the fact that this activationEvent was requested (in case of a restart) this._allRequestedActivateEvents.add(activationEvent); - if (eager) { - return this._activateByEvent(activationEvent, eager); + if (activationKind === ActivationKind.Eager) { + // Do not wait for the normal start-up of the extension host(s) + return this._activateByEvent(activationEvent, activationKind); } - return this._installedExtensionsReady.wait().then(() => this._activateByEvent(activationEvent)); + return this._installedExtensionsReady.wait().then(() => this._activateByEvent(activationEvent, activationKind)); } } - private _activateByEvent(activationEvent: string, eager?: boolean): Promise { + private _activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise { const result = Promise.all( - this._extensionHostManagers.map(extHostManager => extHostManager.activateByEvent(activationEvent, eager)) + this._extensionHostManagers.map(extHostManager => extHostManager.activateByEvent(activationEvent, activationKind)) ).then(() => { }); this._onWillActivateByEvent.fire({ event: activationEvent, diff --git a/src/vs/workbench/services/extensions/common/extensionHostManager.ts b/src/vs/workbench/services/extensions/common/extensionHostManager.ts index e3f7f0c117a..6bad8c1bde3 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostManager.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostManager.ts @@ -21,7 +21,7 @@ import { registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { StopWatch } from 'vs/base/common/stopwatch'; import { VSBuffer } from 'vs/base/common/buffer'; -import { IExtensionHost, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionHost, ExtensionHostKind, ActivationKind } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator'; // Enable to see detailed message communication between window and extension host @@ -76,7 +76,7 @@ export class ExtensionHostManager extends Disposable { } ); this._proxy.then(() => { - initialActivationEvents.forEach((activationEvent) => this.activateByEvent(activationEvent)); + initialActivationEvents.forEach((activationEvent) => this.activateByEvent(activationEvent, ActivationKind.Normal)); this._register(registerLatencyTestProvider({ measure: () => this.measure() })); @@ -219,18 +219,18 @@ export class ExtensionHostManager extends Disposable { return proxy.$activate(extension, reason); } - public activateByEvent(activationEvent: string, eager?: boolean): Promise { - if (eager && !this._hasStarted) { + public activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise { + if (activationKind === ActivationKind.Eager && !this._hasStarted) { return Promise.resolve(); } if (!this._cachedActivationEvents.has(activationEvent)) { - this._cachedActivationEvents.set(activationEvent, this._activateByEvent(activationEvent, eager)); + this._cachedActivationEvents.set(activationEvent, this._activateByEvent(activationEvent, activationKind)); } return this._cachedActivationEvents.get(activationEvent)!; } - private async _activateByEvent(activationEvent: string, eager?: boolean): Promise { + private async _activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise { if (!this._proxy) { return; } @@ -240,7 +240,7 @@ export class ExtensionHostManager extends Disposable { // i.e. the extension host could not be started return; } - return proxy.value.$activateByEvent(activationEvent, eager); + return proxy.value.$activateByEvent(activationEvent, activationKind); } public async getInspectPort(tryEnableInspector: boolean): Promise { diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index f0980b0405c..579f941fecd 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -140,6 +140,11 @@ export interface IResponsiveStateChangeEvent { isResponsive: boolean; } +export const enum ActivationKind { + Normal = 0, + Eager = 1 +} + export interface IExtensionService { readonly _serviceBrand: undefined; @@ -178,12 +183,14 @@ export interface IExtensionService { /** * Send an activation event and activate interested extensions. * - * Normally, this will queue the activation event if the extension hosts are not ready - * and send it to all of them. If the extension needs to be activated before this, - * the eager flag can be used to ignore extension hosts that aren't yet started. Do not - * use this flag unless necessary. + * This will wait for the normal startup of the extension host(s). + * + * In extraordinary circumstances, if the activation event needs to activate + * one or more extensions before the normal startup is finished, then you can use + * `ActivationKind.Eager`. Please do not use this flag unless really necessary + * and you understand all consequences. */ - activateByEvent(activationEvent: string, eager?: boolean): Promise; + activateByEvent(activationEvent: string, activationKind?: ActivationKind): Promise; /** * An promise that resolves when the installed extensions are registered after From df5d0e588ff6956c812b617895e4598ade4db428 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 26 Aug 2020 20:18:10 +0200 Subject: [PATCH 566/736] Rename `ActivationKind.Eager` to `ActivationKind.Immediate` --- src/vs/workbench/api/browser/mainThreadAuthentication.ts | 2 +- src/vs/workbench/api/common/extHostExtensionService.ts | 2 +- .../services/extensions/common/abstractExtensionService.ts | 2 +- .../services/extensions/common/extensionHostManager.ts | 2 +- src/vs/workbench/services/extensions/common/extensions.ts | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts index 18493b01c47..fb2e5ddfeda 100644 --- a/src/vs/workbench/api/browser/mainThreadAuthentication.ts +++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts @@ -249,7 +249,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu } $ensureProvider(id: string): Promise { - return this.extensionService.activateByEvent(getAuthenticationProviderActivationEvent(id), ActivationKind.Eager); + return this.extensionService.activateByEvent(getAuthenticationProviderActivationEvent(id), ActivationKind.Immediate); } $sendDidChangeSessions(id: string, event: modes.AuthenticationSessionsChangeEvent): void { diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 8d5ab4a9ec8..6d9e1a525e0 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -687,7 +687,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme } public $activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise { - if (activationKind === ActivationKind.Eager) { + if (activationKind === ActivationKind.Immediate) { return this._activateByEvent(activationEvent, false); } diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index 5bd8624bc10..c89897ab69c 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -205,7 +205,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx // Record the fact that this activationEvent was requested (in case of a restart) this._allRequestedActivateEvents.add(activationEvent); - if (activationKind === ActivationKind.Eager) { + if (activationKind === ActivationKind.Immediate) { // Do not wait for the normal start-up of the extension host(s) return this._activateByEvent(activationEvent, activationKind); } diff --git a/src/vs/workbench/services/extensions/common/extensionHostManager.ts b/src/vs/workbench/services/extensions/common/extensionHostManager.ts index 6bad8c1bde3..d531ae94a20 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostManager.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostManager.ts @@ -220,7 +220,7 @@ export class ExtensionHostManager extends Disposable { } public activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise { - if (activationKind === ActivationKind.Eager && !this._hasStarted) { + if (activationKind === ActivationKind.Immediate && !this._hasStarted) { return Promise.resolve(); } diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 579f941fecd..83fb70f44b2 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -142,7 +142,7 @@ export interface IResponsiveStateChangeEvent { export const enum ActivationKind { Normal = 0, - Eager = 1 + Immediate = 1 } export interface IExtensionService { @@ -187,7 +187,7 @@ export interface IExtensionService { * * In extraordinary circumstances, if the activation event needs to activate * one or more extensions before the normal startup is finished, then you can use - * `ActivationKind.Eager`. Please do not use this flag unless really necessary + * `ActivationKind.Immediate`. Please do not use this flag unless really necessary * and you understand all consequences. */ activateByEvent(activationEvent: string, activationKind?: ActivationKind): Promise; From b4147335d1458de6a59b00def90208f45e76736f Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 26 Aug 2020 12:06:32 -0700 Subject: [PATCH 567/736] Add setting for cell statusbar visibility #104772 --- .../contrib/notebook/browser/constants.ts | 1 + .../notebook/browser/media/notebook.css | 44 +++++++++---------- .../notebook/browser/notebook.contribution.ts | 7 ++- .../notebook/browser/notebookEditorWidget.ts | 11 +++-- .../browser/view/renderers/cellRenderer.ts | 27 +++++++----- .../browser/view/renderers/codeCell.ts | 10 ++--- .../browser/viewModel/baseCellViewModel.ts | 25 +++++++++-- .../browser/viewModel/codeCellViewModel.ts | 23 +++++----- .../viewModel/markdownCellViewModel.ts | 16 ++++--- .../contrib/notebook/common/notebookCommon.ts | 1 + 10 files changed, 101 insertions(+), 64 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/constants.ts b/src/vs/workbench/contrib/notebook/browser/constants.ts index b77127d1392..245f72a8786 100644 --- a/src/vs/workbench/contrib/notebook/browser/constants.ts +++ b/src/vs/workbench/contrib/notebook/browser/constants.ts @@ -24,6 +24,7 @@ export const CELL_BOTTOM_MARGIN = 6; // Top and bottom padding inside the monaco editor in a cell, which are included in `cell.editorHeight` export const EDITOR_TOP_PADDING = 12; export const EDITOR_BOTTOM_PADDING = 4; +export const EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR = 12; export const CELL_OUTPUT_PADDING = 14; diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 95a5abd483b..87fc0a2edd7 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -350,6 +350,10 @@ position: relative; } +.monaco-workbench .notebookOverlay.cell-statusbar-hidden .cell-statusbar-container { + display: none; +} + .monaco-workbench .notebookOverlay .cell-statusbar-container .cell-status-left { display: flex; flex-grow: 1; @@ -397,46 +401,42 @@ bottom: 0px; top: 0px; } -.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container { - position: relative; +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .run-button-container { + display: flex; + align-items: center; + justify-content: flex-end; + position: absolute; + top: 17px; height: 16px; - flex-shrink: 0; - top: 9px; +} + +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .run-button-container .monaco-toolbar { + visibility: hidden; z-index: 27; /* Above the drag handle */ } -.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container .monaco-toolbar { - visibility: hidden; -} - -.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container .monaco-toolbar .codicon { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .run-button-container .monaco-toolbar .codicon { margin: 0; padding-right: 4px; } -.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container .monaco-toolbar .actions-container { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .run-button-container .monaco-toolbar .actions-container { justify-content: center; } -.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .cell.runnable .run-button-container .monaco-toolbar, -.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused .cell.runnable .run-button-container .monaco-toolbar, -.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-output-hover .cell.runnable .run-button-container .monaco-toolbar { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .runnable .run-button-container .monaco-toolbar, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused .runnable .run-button-container .monaco-toolbar, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-output-hover .runnable .run-button-container .monaco-toolbar { visibility: visible; } -.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .execution-count-label { - position: absolute; +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .execution-count-label { font-size: 10px; font-family: var(--monaco-monospace-font); white-space: pre; - box-sizing: border-box; opacity: .6; - - /* Sizing hacks */ - left: 26px; - width: 35px; - bottom: 0px; - text-align: center; + padding-top: 1px; + margin-right: 1px; } .monaco-workbench .notebookOverlay .cell .cell-editor-part { diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 80db75ecf40..0ad18223bc8 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -30,7 +30,7 @@ import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEd import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { NotebookService } from 'vs/workbench/contrib/notebook/browser/notebookServiceImpl'; -import { CellKind, CellToolbarLocKey, CellUri, DisplayOrderKey, getCellUndoRedoComparisonKey, NotebookDocumentBackupData, NotebookEditorPriority } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellToolbarLocKey, CellUri, DisplayOrderKey, getCellUndoRedoComparisonKey, NotebookDocumentBackupData, NotebookEditorPriority, ShowCellStatusbarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService, IOpenEditorOverride } from 'vs/workbench/services/editor/common/editorService'; @@ -466,6 +466,11 @@ configurationRegistry.registerConfiguration({ type: 'string', enum: ['left', 'right', 'hidden'], default: 'right' + }, + [ShowCellStatusbarKey]: { + description: nls.localize('notebook.showCellStatusbar.description', "Whether the cell statusbar should be shown."), + type: 'boolean', + default: true } } }); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index fc26daa688a..ffa9c892905 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -36,7 +36,7 @@ import { CodeCellRenderer, MarkdownCellRenderer, NotebookCellListDelegate, ListT import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { CellViewModel, IModelDecorationsChangeAccessor, INotebookEditorViewState, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { CellKind, IProcessedOutput, INotebookKernelInfo, INotebookKernelInfoDto, INotebookKernelInfo2, NotebookRunState, NotebookCellRunState, IInsetRenderOutput, CellToolbarLocKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, IProcessedOutput, INotebookKernelInfo, INotebookKernelInfoDto, INotebookKernelInfo2, NotebookRunState, NotebookCellRunState, IInsetRenderOutput, CellToolbarLocKey, ShowCellStatusbarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -245,7 +245,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } } - if (e.affectsConfiguration(CellToolbarLocKey)) { + if (e.affectsConfiguration(CellToolbarLocKey) || e.affectsConfiguration(ShowCellStatusbarKey)) { this._updateForNotebookConfiguration(); } }); @@ -299,6 +299,9 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor if (cellToolbarLocation === 'left' || cellToolbarLocation === 'right' || cellToolbarLocation === 'hidden') { this._overlayContainer.classList.add(`cell-title-toolbar-${cellToolbarLocation}`); } + + const showCellStatusBar = this.configurationService.getValue(ShowCellStatusbarKey); + this._overlayContainer.classList.toggle('cell-statusbar-hidden', !showCellStatusBar); } updateEditorFocus() { @@ -1993,14 +1996,14 @@ registerThemingParticipant((theme, collector) => { // Cell Margin collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell { margin: 0px ${CELL_MARGIN * 2}px 0px ${CELL_MARGIN}px; }`); - collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell.code { margin-left: ${CODE_CELL_LEFT_MARGIN}px; }`); + collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell.code { margin-left: ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`); + collector.addRule(`.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .run-button-container { width: ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`); collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > .cell-inner-container { padding-top: ${CELL_TOP_MARGIN}px; }`); collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row > .cell-inner-container { padding-bottom: ${CELL_BOTTOM_MARGIN}px; }`); collector.addRule(`.notebookOverlay .output { margin: 0px ${CELL_MARGIN}px 0px ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`); collector.addRule(`.notebookOverlay .output { width: calc(100% - ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER + (CELL_MARGIN * 2)}px); }`); collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell.markdown { padding-left: ${CELL_RUN_GUTTER}px; }`); - collector.addRule(`.notebookOverlay .cell .run-button-container { width: 20px; margin: 0px ${Math.floor(CELL_RUN_GUTTER - 20) / 2}px; }`); collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-top { height: ${CELL_TOP_MARGIN}px; }`); collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-side { bottom: ${BOTTOM_CELL_TOOLBAR_GAP}px; }`); collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row.code-cell-row .cell-focus-indicator-left, diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 19a715474a2..f8b512e42cf 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -35,7 +35,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { BOTTOM_CELL_TOOLBAR_GAP, CELL_BOTTOM_MARGIN, CELL_TOP_MARGIN, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; +import { BOTTOM_CELL_TOOLBAR_GAP, CELL_BOTTOM_MARGIN, CELL_TOP_MARGIN, EDITOR_BOTTOM_PADDING, EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; import { CancelCellAction, DeleteCellAction, ExecuteCellAction, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; import { BaseCellRenderTemplate, CellEditState, CodeCellRenderTemplate, EXPAND_CELL_CONTENT_COMMAND_ID, ICellViewModel, INotebookEditor, isCodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys'; @@ -45,7 +45,7 @@ import { StatefulMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { CellKind, NotebookCellMetadata, NotebookCellRunState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, NotebookCellMetadata, NotebookCellRunState, ShowCellStatusbarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { createAndFillInActionBarActionsWithVerticalSeparators, VerticalSeparator, VerticalSeparatorViewItem } from './cellActionView'; import { CodiconActionViewItem, CellLanguageStatusBarItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents'; import { CellDragAndDropController, DRAGGING_CLASS } from 'vs/workbench/contrib/notebook/browser/view/renderers/dnd'; @@ -82,10 +82,6 @@ export class NotebookCellListDelegate implements IListVirtualDelegate { - if (e.affectsConfiguration('editor')) { + if (e.affectsConfiguration('editor') || e.affectsConfiguration(ShowCellStatusbarKey)) { this._value = computeEditorOptions(); this._onDidChange.fire(this.value); } }); const computeEditorOptions = () => { + const showCellStatusBar = configurationService.getValue(ShowCellStatusbarKey); + const editorPadding = { + top: EDITOR_TOP_PADDING, + bottom: showCellStatusBar ? EDITOR_BOTTOM_PADDING : EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR + }; + const editorOptions = deepClone(configurationService.getValue('editor', { overrideIdentifier: language })); const computed = { ...editorOptions, - ...CellEditorOptions.fixedEditorOptions + ...CellEditorOptions.fixedEditorOptions, + ...{ padding: editorPadding } }; if (!computed.folding) { @@ -660,10 +663,10 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende const dragHandle = DOM.append(container, DOM.$('.cell-drag-handle')); const cellContainer = DOM.append(container, $('.cell.code')); - const runButtonContainer = DOM.append(cellContainer, $('.run-button-container')); - const runToolbar = disposables.add(this.createToolbar(runButtonContainer)); - const executionOrderLabel = DOM.append(cellContainer, $('div.execution-count-label')); + const runButtonContainer = DOM.append(container, $('.run-button-container')); + const runToolbar = disposables.add(this.createToolbar(runButtonContainer)); + const executionOrderLabel = DOM.append(runButtonContainer, $('div.execution-count-label')); // create a special context key service that set the inCompositeEditor-contextkey const editorContextKeyService = disposables.add(this.contextKeyServiceProvider(container)); @@ -761,7 +764,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende private updateForMetadata(element: CodeCellViewModel, templateData: CodeCellRenderTemplate): void { const metadata = element.getEvaluatedMetadata(this.notebookEditor.viewModel!.notebookDocument.metadata); - DOM.toggleClass(templateData.cellContainer, 'runnable', !!metadata.runnable); + DOM.toggleClass(templateData.container, 'runnable', !!metadata.runnable); this.updateExecutionOrder(metadata, templateData); templateData.cellStatusMessageContainer.textContent = metadata?.statusMessage || ''; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index 480e928baad..3f449a6a7ab 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -4,21 +4,21 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { raceCancellation } from 'vs/base/common/async'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { IDimension } from 'vs/editor/common/editorCommon'; import { IModeService } from 'vs/editor/common/services/modeService'; import * as nls from 'vs/nls'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; import { CellFocusMode, CodeCellRenderTemplate, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; -import { CellOutputKind, IProcessedOutput, IRenderOutput, ITransformedDisplayOutputDto, BUILTIN_RENDERER_ID, RenderOutputType, outputHasDynamicHeight } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { KeyCode } from 'vs/base/common/keyCodes'; -import { IDimension } from 'vs/editor/common/editorCommon'; +import { BUILTIN_RENDERER_ID, CellOutputKind, IProcessedOutput, IRenderOutput, ITransformedDisplayOutputDto, outputHasDynamicHeight, RenderOutputType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; interface IMimeTypeRenderer extends IQuickPickItem { index: number; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts index d70f6e4c842..d145744d489 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts @@ -12,12 +12,14 @@ import { IPosition } from 'vs/editor/common/core/position'; import * as editorCommon from 'vs/editor/common/editorCommon'; import * as model from 'vs/editor/common/model'; import { SearchParams } from 'vs/editor/common/model/textModelSearch'; -import { EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; +import { CELL_STATUSBAR_HEIGHT, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; import { CellEditState, CellFocusMode, CursorAtBoundary, CellViewModelStateChangeEvent, IEditableCellViewModel, INotebookCellDecorationOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { CellKind, NotebookCellMetadata, NotebookDocumentMetadata, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, NotebookCellMetadata, NotebookDocumentMetadata, INotebookSearchOptions, ShowCellStatusbarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export abstract class BaseCellViewModel extends Disposable { + protected readonly _onDidChangeEditorAttachState = new Emitter(); // Do not merge this event with `onDidChangeState` as we are using `Event.once(onDidChangeEditorAttachState)` elsewhere. readonly onDidChangeEditorAttachState = this._onDidChangeEditorAttachState.event; @@ -106,7 +108,12 @@ export abstract class BaseCellViewModel extends Disposable { this._dragging = v; } - constructor(readonly viewType: string, readonly model: NotebookCellTextModel, public id: string) { + constructor( + readonly viewType: string, + readonly model: NotebookCellTextModel, + public id: string, + private readonly _configurationService: IConfigurationService + ) { super(); this._register(model.onDidChangeLanguage(() => { @@ -116,12 +123,24 @@ export abstract class BaseCellViewModel extends Disposable { this._register(model.onDidChangeMetadata(() => { this._onDidChangeState.fire({ metadataChanged: true }); })); + + this._register(this._configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(ShowCellStatusbarKey)) { + this.layoutChange({}); + } + })); + } + + protected getEditorStatusbarHeight() { + const showCellStatusBar = this._configurationService.getValue(ShowCellStatusbarKey); + return showCellStatusBar ? CELL_STATUSBAR_HEIGHT : 0; } // abstract resolveTextModel(): Promise; abstract hasDynamicHeight(): boolean; abstract getHeight(lineHeight: number): number; abstract onDeselect(): void; + abstract layoutChange(change: any): void; assertTextModelAttached(): boolean { if (this.textModel && this._textEditor && this._textEditor.getModel() === this.textModel) { diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts index cca77b7d78e..0a7c21cf9be 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts @@ -8,12 +8,13 @@ import * as UUID from 'vs/base/common/uuid'; import * as editorCommon from 'vs/editor/common/editorCommon'; import * as model from 'vs/editor/common/model'; import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; -import { BOTTOM_CELL_TOOLBAR_GAP, CELL_MARGIN, CELL_RUN_GUTTER, CELL_STATUSBAR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, CELL_TOP_MARGIN, EDITOR_TOP_PADDING, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN, BOTTOM_CELL_TOOLBAR_HEIGHT, COLLAPSED_INDICATOR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellEditState, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, ICellViewModel, NotebookLayoutInfo, CodeCellLayoutState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { CellKind, NotebookCellOutputsSplice, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { BaseCellViewModel } from './baseCellViewModel'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { BOTTOM_CELL_TOOLBAR_GAP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_MARGIN, CELL_RUN_GUTTER, CELL_TOP_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; +import { CellEditState, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, CodeCellLayoutState, ICellViewModel, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; +import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; +import { CellKind, INotebookSearchOptions, NotebookCellOutputsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { BaseCellViewModel } from './baseCellViewModel'; export class CodeCellViewModel extends BaseCellViewModel implements ICellViewModel { readonly cellKind = CellKind.Code; @@ -68,9 +69,10 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod readonly viewType: string, readonly model: NotebookCellTextModel, initialNotebookLayoutInfo: NotebookLayoutInfo | null, - readonly eventDispatcher: NotebookEventDispatcher + readonly eventDispatcher: NotebookEventDispatcher, + @IConfigurationService configurationService: IConfigurationService ) { - super(viewType, model, UUID.generateUuid()); + super(viewType, model, UUID.generateUuid(), configurationService); this._register(this.model.onDidChangeOutputs((splices) => { this._outputCollection = new Array(this.model.outputs.length); this._outputsTop = null; @@ -121,8 +123,9 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod newState = CodeCellLayoutState.Estimated; } - const indicatorHeight = editorHeight + CELL_STATUSBAR_HEIGHT + outputTotalHeight; - const outputContainerOffset = EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + CELL_STATUSBAR_HEIGHT; + const statusbarHeight = this.getEditorStatusbarHeight(); + const indicatorHeight = editorHeight + statusbarHeight + outputTotalHeight; + const outputContainerOffset = EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + statusbarHeight; const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2; const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo?.editorWidth; @@ -209,7 +212,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod } private computeTotalHeight(editorHeight: number, outputsTotalHeight: number): number { - return EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + CELL_STATUSBAR_HEIGHT + outputsTotalHeight + BOTTOM_CELL_TOOLBAR_GAP + CELL_BOTTOM_MARGIN; + return EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + this.getEditorStatusbarHeight() + outputsTotalHeight + BOTTOM_CELL_TOOLBAR_GAP + CELL_BOTTOM_MARGIN; } /** diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts index 86f9720eac8..2916dcd868e 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts @@ -3,19 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; import { Emitter, Event } from 'vs/base/common/event'; import * as UUID from 'vs/base/common/uuid'; import * as editorCommon from 'vs/editor/common/editorCommon'; import * as model from 'vs/editor/common/model'; -import { BOTTOM_CELL_TOOLBAR_GAP, CELL_MARGIN, CELL_STATUSBAR_HEIGHT, CELL_TOP_MARGIN, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN, BOTTOM_CELL_TOOLBAR_HEIGHT, COLLAPSED_INDICATOR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; +import * as nls from 'vs/nls'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { BOTTOM_CELL_TOOLBAR_GAP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_MARGIN, CELL_TOP_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; +import { EditorFoldingStateDelegate } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel'; import { CellFindMatch, ICellViewModel, MarkdownCellLayoutChangeEvent, MarkdownCellLayoutInfo, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { MarkdownRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/mdRenderer'; import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel'; -import { EditorFoldingStateDelegate } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel'; +import { NotebookCellStateChangedEvent, NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { CellKind, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { NotebookEventDispatcher, NotebookCellStateChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; export class MarkdownCellViewModel extends BaseCellViewModel implements ICellViewModel { readonly cellKind = CellKind.Markdown; @@ -45,7 +46,7 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie set editorHeight(newHeight: number) { this._editorHeight = newHeight; - this.totalHeight = this._editorHeight + CELL_TOP_MARGIN + CELL_BOTTOM_MARGIN + BOTTOM_CELL_TOOLBAR_GAP + CELL_STATUSBAR_HEIGHT; + this.totalHeight = this._editorHeight + CELL_TOP_MARGIN + CELL_BOTTOM_MARGIN + BOTTOM_CELL_TOOLBAR_GAP + this.getEditorStatusbarHeight(); } get editorHeight() { @@ -65,9 +66,10 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie initialNotebookLayoutInfo: NotebookLayoutInfo | null, readonly foldingDelegate: EditorFoldingStateDelegate, readonly eventDispatcher: NotebookEventDispatcher, - private readonly _mdRenderer: MarkdownRenderer + private readonly _mdRenderer: MarkdownRenderer, + @IConfigurationService configurationService: IConfigurationService ) { - super(viewType, model, UUID.generateUuid()); + super(viewType, model, UUID.generateUuid(), configurationService); this._layoutInfo = { editorHeight: 0, diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 2505a5c855d..81487436421 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -766,3 +766,4 @@ export interface INotebookDiffResult { } export const DisplayOrderKey = 'notebook.displayOrder'; export const CellToolbarLocKey = 'notebook.cellToolbarLocation'; +export const ShowCellStatusbarKey = 'notebook.showCellStatusbar'; From b08a4042ac3bc39cfa34d2bf092104a436c1313f Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 26 Aug 2020 12:10:58 -0700 Subject: [PATCH 568/736] support editing of cell content --- .../notebook/browser/diff/cellComponents.ts | 98 ++----------------- .../browser/diff/notebookTextDiffEditor.ts | 2 +- .../browser/diff/notebookTextDiffList.ts | 4 +- 3 files changed, 10 insertions(+), 94 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index f339970b91e..44a4b1a6ea9 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -53,6 +53,7 @@ const fixedDiffEditorOptions: IDiffEditorOptions = { glyphMargin: true, enableSplitViewResizing: false, renderIndicators: false, + readOnly: false }; @@ -355,8 +356,8 @@ abstract class AbstractCellRenderer extends Disposable { if (originalMetadataSource !== modifiedMetadataSource) { this._metadataEditor = this.instantiationService.createInstance(DiffEditorWidget, this._metadataEditorContainer!, { ...fixedDiffEditorOptions, - overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode() - + overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode(), + readOnly: true }); DOM.addClass(this._metadataEditorContainer!, 'diff'); @@ -427,7 +428,8 @@ abstract class AbstractCellRenderer extends Disposable { if (originalOutputsSource !== modifiedOutputsSource) { this._outputEditor = this.instantiationService.createInstance(DiffEditorWidget, this._outputEditorContainer!, { ...fixedDiffEditorOptions, - overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode() + overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode(), + readOnly: true }); DOM.addClass(this._outputEditorContainer!, 'diff'); @@ -504,93 +506,6 @@ abstract class AbstractCellRenderer extends Disposable { abstract layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean, outputEditor?: boolean }): void; } -export class UnchangedCell extends AbstractCellRenderer { - private _editor!: CodeEditorWidget; - - constructor( - readonly notebookEditor: INotebookTextDiffEditor, - readonly cell: CellDiffViewModel, - readonly templateData: CellDiffRenderTemplate, - @IModeService readonly modeService: IModeService, - @IModelService protected modelService: IModelService, - @IInstantiationService protected readonly instantiationService: IInstantiationService, - ) { - super(notebookEditor, cell, templateData, 'full', instantiationService, modeService, modelService); - } - - initData() { - } - - styleContainer(container: HTMLElement) { - } - - buildSourceEditor(sourceContainer: HTMLElement) { - const editorContainer = DOM.append(sourceContainer, DOM.$('.editor-container')); - const originalCell = this.cell.original!; - const lineCount = originalCell.textBuffer.getLineCount(); - const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; - const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; - - this._editor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, { - ...fixedEditorOptions, - dimension: { - width: this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN, - height: editorHeight - }, - overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode() - }, {}); - - this._register(this._editor.onDidContentSizeChange((e) => { - if (e.contentHeightChanged) { - this._layoutInfo.editorHeight = e.contentHeight; - this.layout({ editorHeight: true }); - } - })); - - originalCell.resolveTextModelRef().then(ref => { - this._register(ref); - - const textModel = ref.object.textEditorModel; - this._editor.setModel(textModel); - - this._layoutInfo.editorHeight = this._editor.getContentHeight(); - this.layout({ editorHeight: true }); - }); - } - - - onDidLayoutChange(event: CellDiffViewModelLayoutChangeEvent): void { - if (event.outerWidth !== undefined) { - this.layout({ outerWidth: true }); - } - } - - layout(state: { outerWidth?: boolean, editorHeight?: boolean, metadataEditor?: boolean, outputEditor?: boolean }) { - if (state.editorHeight || state.outerWidth) { - this._editor.layout({ - width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true), - height: this._layoutInfo.editorHeight - }); - } - - if (state.metadataEditor || state.outerWidth) { - this._metadataEditor?.layout({ - width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true), - height: this._layoutInfo.metadataHeight - }); - } - - if (state.outputEditor || state.outerWidth) { - this._outputEditor?.layout({ - width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true), - height: this._layoutInfo.outputHeight - }); - } - - this.layoutNotebookCell(); - } -} - export class DeletedCell extends AbstractCellRenderer { private _editor!: CodeEditorWidget; constructor( @@ -794,7 +709,8 @@ export class ModifiedCell extends AbstractCellRenderer { this._editor = this.instantiationService.createInstance(DiffEditorWidget, this._editorContainer, { ...fixedDiffEditorOptions, - overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode() + overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode(), + originalEditable: false }); DOM.addClass(this._editorContainer, 'diff'); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index de6076b0ff3..72e3d478988 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -163,7 +163,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD if (originalCell.getHashValue() === modifiedCell.getHashValue()) { cellDiffViewModels.push(new CellDiffViewModel( originalCell, - undefined, + modifiedCell, 'unchanged', this._eventDispatcher! )); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts index 705c261a2cc..2931710c259 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts @@ -17,7 +17,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { CellDiffViewModel } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; import { CellDiffRenderTemplate, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/common'; import { isMacintosh } from 'vs/base/common/platform'; -import { UnchangedCell, DeletedCell, InsertCell, ModifiedCell } from 'vs/workbench/contrib/notebook/browser/diff/cellComponents'; +import { DeletedCell, InsertCell, ModifiedCell } from 'vs/workbench/contrib/notebook/browser/diff/cellComponents'; export class NotebookCellTextDiffListDelegate implements IListVirtualDelegate { // private readonly lineHeight: number; @@ -64,7 +64,7 @@ export class CellDiffRenderer implements IListRenderer Date: Wed, 26 Aug 2020 12:12:14 -0700 Subject: [PATCH 569/736] allow editing of insert cell content. --- .../workbench/contrib/notebook/browser/diff/cellComponents.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index 44a4b1a6ea9..868c3ab9a8e 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -626,7 +626,8 @@ export class InsertCell extends AbstractCellRenderer { width: (this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN) / 2 - 18, height: editorHeight }, - overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode() + overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode(), + readOnly: false }, {}); this._layoutInfo.editorHeight = editorHeight; From 2872bf2525d122cb72209ec2ad2ad3a5245c492a Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 26 Aug 2020 12:34:52 -0700 Subject: [PATCH 570/736] Fix tests --- .../browser/contrib/fold/test/notebookFolding.test.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/fold/test/notebookFolding.test.ts b/src/vs/workbench/contrib/notebook/browser/contrib/fold/test/notebookFolding.test.ts index 3dbd7eadc7b..51d77a18fbd 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/fold/test/notebookFolding.test.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/fold/test/notebookFolding.test.ts @@ -4,9 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { withTestNotebook } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; +import { setupInstantiationService, withTestNotebook } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { FoldingModel } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel'; @@ -17,7 +16,7 @@ function updateFoldingStateAtIndex(foldingModel: FoldingModel, index: number, co } suite('Notebook Folding', () => { - const instantiationService = new TestInstantiationService(); + const instantiationService = setupInstantiationService(); const blukEditService = instantiationService.get(IBulkEditService); const undoRedoService = instantiationService.stub(IUndoRedoService, () => { }); instantiationService.spy(IUndoRedoService, 'pushElement'); From 1ace7f22a63c8e8a6df61845f524af1d564a0953 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 26 Aug 2020 14:50:18 -0700 Subject: [PATCH 571/736] Fix run button/execution order labels when cell is collapsed --- .../contrib/notebook/browser/view/renderers/codeCell.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index 3f449a6a7ab..1c0c22f4482 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -341,6 +341,7 @@ export class CodeCell extends Disposable { private viewUpdateInputCollapsed(): void { DOM.hide(this.templateData.cellContainer); + DOM.hide(this.templateData.runButtonContainer); DOM.show(this.templateData.collapsedPart); DOM.show(this.templateData.outputContainer); this.templateData.container.classList.toggle('collapsed', true); @@ -358,6 +359,7 @@ export class CodeCell extends Disposable { private viewUpdateOutputCollapsed(): void { DOM.show(this.templateData.cellContainer); + DOM.show(this.templateData.runButtonContainer); DOM.show(this.templateData.collapsedPart); DOM.hide(this.templateData.outputContainer); @@ -371,6 +373,7 @@ export class CodeCell extends Disposable { private viewUpdateAllCollapsed(): void { DOM.hide(this.templateData.cellContainer); + DOM.hide(this.templateData.runButtonContainer); DOM.show(this.templateData.collapsedPart); DOM.hide(this.templateData.outputContainer); this.templateData.container.classList.toggle('collapsed', true); @@ -385,6 +388,7 @@ export class CodeCell extends Disposable { private viewUpdateExpanded(): void { DOM.show(this.templateData.cellContainer); + DOM.show(this.templateData.runButtonContainer); DOM.hide(this.templateData.collapsedPart); DOM.show(this.templateData.outputContainer); this.templateData.container.classList.toggle('collapsed', false); From 4f210e60aed2cb9456999df16be8d151fcc1e745 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 26 Aug 2020 15:13:03 -0700 Subject: [PATCH 572/736] allow metadata apply even if it does not trigger content change --- .../contrib/notebook/common/model/notebookTextModel.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index b85e95045cb..a27d7e8d218 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -525,10 +525,6 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel return; } - if (!this._isCellMetadataChanged(cell.metadata, metadata)) { - return; - } - const triggerDirtyChange = this._isCellMetadataChanged(cell.metadata, metadata); if (triggerDirtyChange) { From a6103e8faaf94a5e52b9ea2a38e9ae5bfbdc7adb Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Wed, 26 Aug 2020 15:20:34 -0700 Subject: [PATCH 573/736] Fix Buffer reference in browser for ms auth --- extensions/microsoft-authentication/src/AADHelper.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts index 035c5af7350..67b55889c3c 100644 --- a/extensions/microsoft-authentication/src/AADHelper.ts +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -5,6 +5,7 @@ import * as randomBytes from 'randombytes'; import * as querystring from 'querystring'; +import { Buffer } from 'buffer'; import * as vscode from 'vscode'; import { createServer, startServer } from './authServer'; From 4d88b39b7d49ea13eacbe805e2991a0f848c5109 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 26 Aug 2020 14:40:17 -0700 Subject: [PATCH 574/736] Split up main thread webview services --- .../api/browser/extensionHost.contribution.ts | 2 +- .../api/browser/mainThreadCustomEditors.ts | 28 ++- ....ts => mainThreadWebviewPanelsAndViews.ts} | 167 +++--------------- .../browser/mainThreadWebviewSerializer.ts | 15 +- .../api/browser/mainThreadWebviewViews.ts | 4 +- .../api/browser/mainThreadWebviews.ts | 125 +++++++++++++ .../workbench/api/common/extHost.protocol.ts | 18 +- .../api/common/extHostCustomEditors.ts | 4 +- src/vs/workbench/api/common/extHostWebview.ts | 18 +- .../api/common/extHostWebviewSerializer.ts | 4 +- .../api/common/extHostWebviewView.ts | 8 +- .../test/browser/api/extHostWebview.test.ts | 4 +- 12 files changed, 209 insertions(+), 188 deletions(-) rename src/vs/workbench/api/browser/{mainThreadWebview.ts => mainThreadWebviewPanelsAndViews.ts} (61%) create mode 100644 src/vs/workbench/api/browser/mainThreadWebviews.ts diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index 3d77009b908..5a9674d46bc 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -54,7 +54,7 @@ import './mainThreadTreeViews'; import './mainThreadDownloadService'; import './mainThreadUrls'; import './mainThreadWindow'; -import './mainThreadWebview'; +import './mainThreadWebviewPanelsAndViews'; import './mainThreadWorkspace'; import './mainThreadComments'; import './mainThreadNotebook'; diff --git a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts index 848f4a142ed..b3d73594e6f 100644 --- a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts @@ -19,7 +19,8 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo'; -import type { MainThreadWebviews } from 'vs/workbench/api/browser/mainThreadWebview'; +import type { MainThreadWebviewPanelsAndViews } from 'vs/workbench/api/browser/mainThreadWebviewPanelsAndViews'; +import { MainThreadWebviews, reviveWebviewExtension } from 'vs/workbench/api/browser/mainThreadWebviews'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { editorGroupToViewColumn } from 'vs/workbench/api/common/shared/editor'; import { IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor'; @@ -35,19 +36,20 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { IWorkingCopy, IWorkingCopyBackup, IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; -export const enum CustomEditorModelType { +const enum CustomEditorModelType { Custom, Text, } -export class MainThreadCustomEditors extends Disposable { +export class MainThreadCustomEditors extends Disposable implements extHostProtocol.MainThreadCustomEditorsShape { private readonly _proxyCustomEditors: extHostProtocol.ExtHostCustomEditorsShape; private readonly _editorProviders = new Map(); constructor( - private readonly mainThreadWebviews: MainThreadWebviews, + private readonly mainThreadWebview: MainThreadWebviews, + private readonly mainThreadWebviewPanelsAndViews: MainThreadWebviewPanelsAndViews, context: extHostProtocol.IExtHostContext, @IWorkingCopyService workingCopyService: IWorkingCopyService, @IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService, @@ -85,7 +87,15 @@ export class MainThreadCustomEditors extends Disposable { this._editorProviders.clear(); } - public registerEditorProvider( + public $registerTextEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: extHostProtocol.CustomTextEditorCapabilities): void { + this.registerEditorProvider(CustomEditorModelType.Text, reviveWebviewExtension(extensionData), viewType, options, capabilities, true); + } + + public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void { + this.registerEditorProvider(CustomEditorModelType.Custom, reviveWebviewExtension(extensionData), viewType, options, {}, supportsMultipleEditorsPerDocument); + } + + private registerEditorProvider( modelType: CustomEditorModelType, extension: WebviewExtensionDescription, viewType: string, @@ -111,7 +121,7 @@ export class MainThreadCustomEditors extends Disposable { const handle = webviewInput.id; const resource = webviewInput.resource; - this.mainThreadWebviews.addWebviewInput(handle, webviewInput); + this.mainThreadWebviewPanelsAndViews.addWebviewInput(handle, webviewInput); webviewInput.webview.options = options; webviewInput.webview.extension = extension; @@ -120,7 +130,7 @@ export class MainThreadCustomEditors extends Disposable { modelRef = await this.getOrCreateCustomEditorModel(modelType, resource, viewType, { backupId: webviewInput.backupId }, cancellation); } catch (error) { onUnexpectedError(error); - webviewInput.webview.html = this.mainThreadWebviews.getWebviewResolvedFailedContent(viewType); + webviewInput.webview.html = this.mainThreadWebview.getWebviewResolvedFailedContent(viewType); return; } @@ -157,7 +167,7 @@ export class MainThreadCustomEditors extends Disposable { await this._proxyCustomEditors.$resolveWebviewEditor(resource, handle, viewType, webviewInput.getTitle(), editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options, cancellation); } catch (error) { onUnexpectedError(error); - webviewInput.webview.html = this.mainThreadWebviews.getWebviewResolvedFailedContent(viewType); + webviewInput.webview.html = this.mainThreadWebview.getWebviewResolvedFailedContent(viewType); modelRef.dispose(); return; } @@ -200,7 +210,7 @@ export class MainThreadCustomEditors extends Disposable { case CustomEditorModelType.Custom: { const model = MainThreadCustomEditorModel.create(this._instantiationService, this._proxyCustomEditors, viewType, resource, options, () => { - return Array.from(this.mainThreadWebviews.webviewInputs) + return Array.from(this.mainThreadWebviewPanelsAndViews.webviewInputs) .filter(editor => editor instanceof CustomEditorInput && isEqual(editor.resource, resource)) as CustomEditorInput[]; }, cancellation, this._backupService); return this._customEditorService.models.add(resource, viewType, model); diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebviewPanelsAndViews.ts similarity index 61% rename from src/vs/workbench/api/browser/mainThreadWebview.ts rename to src/vs/workbench/api/browser/mainThreadWebviewPanelsAndViews.ts index ec9c8e18ef5..d019b03336c 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewPanelsAndViews.ts @@ -4,25 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { Schemas } from 'vs/base/common/network'; -import { isWeb } from 'vs/base/common/platform'; -import { escape } from 'vs/base/common/strings'; import { URI, UriComponents } from 'vs/base/common/uri'; -import * as modes from 'vs/editor/common/modes'; -import { localize } from 'vs/nls'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IProductService } from 'vs/platform/product/common/productService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { CustomEditorModelType, MainThreadCustomEditors } from 'vs/workbench/api/browser/mainThreadCustomEditors'; +import { MainThreadCustomEditors } from 'vs/workbench/api/browser/mainThreadCustomEditors'; +import { MainThreadWebviews, reviveWebviewExtension, reviveWebviewOptions } from 'vs/workbench/api/browser/mainThreadWebviews'; import { MainThreadWebviewSerializers } from 'vs/workbench/api/browser/mainThreadWebviewSerializer'; import { MainThreadWebviewsViews } from 'vs/workbench/api/browser/mainThreadWebviewViews'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; import { IEditorInput } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; -import { Webview, WebviewExtensionDescription, WebviewIcons, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; +import { WebviewIcons } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; import { ICreateWebViewShowOptions, IWebviewWorkbenchService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -82,37 +75,26 @@ class WebviewViewTypeTransformer { } } -@extHostNamedCustomer(extHostProtocol.MainContext.MainThreadWebviews) -export class MainThreadWebviews extends Disposable implements extHostProtocol.MainThreadWebviewsShape { - - private static readonly standardSupportedLinkSchemes = new Set([ - Schemas.http, - Schemas.https, - Schemas.mailto, - Schemas.vscode, - 'vscode-insider', - ]); +@extHostNamedCustomer(extHostProtocol.MainContext.MainThreadWebviewService) +export class MainThreadWebviewPanelsAndViews extends Disposable implements extHostProtocol.MainThreadWebviewPanelsAndViewsShape { public readonly webviewPanelViewType = new WebviewViewTypeTransformer('mainThreadWebview-'); private readonly _proxy: extHostProtocol.ExtHostWebviewsShape; - private readonly _webviews = new Map(); private readonly _webviewInputs = new WebviewInputStore(); private readonly _editorProviders = new Map(); private readonly _webviewFromDiffEditorHandles = new Set(); + private readonly _mainThreadWebviews: MainThreadWebviews; + private readonly serializers: MainThreadWebviewSerializers; - private readonly customEditors: MainThreadCustomEditors; - private readonly webviewViews: MainThreadWebviewsViews; constructor( context: extHostProtocol.IExtHostContext, @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, @IEditorService private readonly _editorService: IEditorService, - @IOpenerService private readonly _openerService: IOpenerService, - @IProductService private readonly _productService: IProductService, @ITelemetryService private readonly _telemetryService: ITelemetryService, @IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @@ -121,9 +103,16 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviews); - this.serializers = this._instantiationService.createInstance(MainThreadWebviewSerializers, this, context); - this.customEditors = this._instantiationService.createInstance(MainThreadCustomEditors, this, context); - this.webviewViews = this._instantiationService.createInstance(MainThreadWebviewsViews, this, context); + this._mainThreadWebviews = this._instantiationService.createInstance(MainThreadWebviews, context); + context.set(extHostProtocol.MainContext.MainThreadWebviews, this._mainThreadWebviews); + + const webviewViews = this._instantiationService.createInstance(MainThreadWebviewsViews, this._mainThreadWebviews, context); + context.set(extHostProtocol.MainContext.MainThreadWebviewViews, webviewViews); + + const customEditors = this._instantiationService.createInstance(MainThreadCustomEditors, this._mainThreadWebviews, this, context); + context.set(extHostProtocol.MainContext.MainThreadCustomEditors, customEditors); + + this.serializers = this._instantiationService.createInstance(MainThreadWebviewSerializers, this._mainThreadWebviews, this, context); this._register(_editorService.onDidActiveEditorChange(() => { const activeInput = this._editorService.activeEditor; @@ -152,12 +141,13 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma public addWebviewInput(handle: extHostProtocol.WebviewPanelHandle, input: WebviewInput): void { this._webviewInputs.add(handle, input); - this.addWebview(handle, input.webview); - } + this._mainThreadWebviews.addWebview(handle, input.webview); - public addWebview(handle: extHostProtocol.WebviewPanelHandle, webview: WebviewOverlay): void { - this._webviews.set(handle, webview); - this.hookupWebviewEventDelegate(handle, webview); + input.webview.onDispose(() => { + this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => { + this._webviewInputs.delete(handle); + }); + }); } public $createWebviewPanel( @@ -197,25 +187,12 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma webview.setName(value); } - public $setWebviewViewTitle(handle: extHostProtocol.WebviewPanelHandle, value: string | undefined): void { - this.webviewViews.$setWebviewViewTitle(handle, value); - } public $setIconPath(handle: extHostProtocol.WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents; } | undefined): void { const webview = this.getWebviewInput(handle); webview.iconPath = reviveWebviewIcon(value); } - public $setHtml(handle: extHostProtocol.WebviewPanelHandle, value: string): void { - const webview = this.getWebview(handle); - webview.html = value; - } - - public $setOptions(handle: extHostProtocol.WebviewPanelHandle, options: modes.IWebviewOptions): void { - const webview = this.getWebview(handle); - webview.contentOptions = reviveWebviewOptions(options); - } - public $reveal(handle: extHostProtocol.WebviewPanelHandle, showOptions: extHostProtocol.WebviewPanelShowOptions): void { const webview = this.getWebviewInput(handle); if (webview.isDisposed()) { @@ -228,12 +205,6 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma } } - public async $postMessage(handle: extHostProtocol.WebviewPanelHandle, message: any): Promise { - const webview = this.getWebview(handle); - webview.postMessage(message); - return true; - } - public $registerSerializer(viewType: string): void { this.serializers.$registerSerializer(viewType); } @@ -242,51 +213,6 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma this.serializers.$unregisterSerializer(viewType); } - public $registerWebviewViewProvider(viewType: string, options?: { retainContextWhenHidden?: boolean }): void { - this.webviewViews.$registerWebviewViewProvider(viewType, options); - } - - public $unregisterWebviewViewProvider(viewType: string): void { - this.webviewViews.$unregisterWebviewViewProvider(viewType); - } - - public $registerTextEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: extHostProtocol.CustomTextEditorCapabilities): void { - this.customEditors.registerEditorProvider(CustomEditorModelType.Text, reviveWebviewExtension(extensionData), viewType, options, capabilities, true); - } - - public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void { - this.customEditors.registerEditorProvider(CustomEditorModelType.Custom, reviveWebviewExtension(extensionData), viewType, options, {}, supportsMultipleEditorsPerDocument); - } - - public $unregisterEditorProvider(viewType: string): void { - this.customEditors.$unregisterEditorProvider(viewType); - } - - public async $onDidEdit(resourceComponents: UriComponents, viewType: string, editId: number, label: string | undefined): Promise { - this.customEditors.$onDidEdit(resourceComponents, viewType, editId, label); - } - - public async $onContentChange(resourceComponents: UriComponents, viewType: string): Promise { - this.customEditors.$onContentChange(resourceComponents, viewType); - } - - public hookupWebviewEventDelegate(handle: extHostProtocol.WebviewPanelHandle, webview: WebviewOverlay) { - const disposables = new DisposableStore(); - - disposables.add(webview.onDidClickLink((uri) => this.onDidClickLink(handle, uri))); - disposables.add(webview.onMessage((message: any) => { this._proxy.$onMessage(handle, message); })); - disposables.add(webview.onMissingCsp((extension: ExtensionIdentifier) => this._proxy.$onMissingCsp(handle, extension.value))); - - disposables.add(webview.onDispose(() => { - disposables.dispose(); - - this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => { - this._webviews.delete(handle); - this._webviewInputs.delete(handle); - }); - })); - } - private registerWebviewFromDiffEditorListeners(diffEditorInput: DiffEditorInput): void { const primary = diffEditorInput.primary as WebviewInput; const secondary = diffEditorInput.secondary as WebviewInput; @@ -348,31 +274,6 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma } } - private onDidClickLink(handle: extHostProtocol.WebviewPanelHandle, link: string): void { - const webview = this.getWebviewInput(handle); - if (this.isSupportedLink(webview, URI.parse(link))) { - this._openerService.open(link, { fromUserGesture: true }); - } - } - - private isSupportedLink(webview: WebviewInput, link: URI): boolean { - if (MainThreadWebviews.standardSupportedLinkSchemes.has(link.scheme)) { - return true; - } - if (!isWeb && this._productService.urlProtocol === link.scheme) { - return true; - } - return !!webview.webview.contentOptions.enableCommandUris && link.scheme === Schemas.command; - } - - private getWebview(handle: extHostProtocol.WebviewPanelHandle): Webview { - const webview = this._webviews.get(handle); - if (!webview) { - throw new Error(`Unknown webview handle:${handle}`); - } - return webview; - } - private getWebviewInput(handle: extHostProtocol.WebviewPanelHandle): WebviewInput { const webview = this.tryGetWebviewInput(handle); if (!webview) { @@ -384,30 +285,8 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma private tryGetWebviewInput(handle: extHostProtocol.WebviewPanelHandle): WebviewInput | undefined { return this._webviewInputs.getInputForHandle(handle); } - - public getWebviewResolvedFailedContent(viewType: string) { - return ` - - - - - - ${localize('errorMessage', "An error occurred while loading view: {0}", escape(viewType))} - `; - } } -function reviveWebviewExtension(extensionData: extHostProtocol.WebviewExtensionDescription): WebviewExtensionDescription { - return { id: extensionData.id, location: URI.revive(extensionData.location) }; -} - -function reviveWebviewOptions(options: modes.IWebviewOptions): WebviewInputOptions { - return { - ...options, - allowScripts: options.enableScripts, - localResourceRoots: Array.isArray(options.localResourceRoots) ? options.localResourceRoots.map(r => URI.revive(r)) : undefined, - }; -} function reviveWebviewIcon( value: { light: UriComponents, dark: UriComponents; } | undefined diff --git a/src/vs/workbench/api/browser/mainThreadWebviewSerializer.ts b/src/vs/workbench/api/browser/mainThreadWebviewSerializer.ts index f72f472d6ce..ee585c7298d 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewSerializer.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewSerializer.ts @@ -5,7 +5,8 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import type { MainThreadWebviews } from 'vs/workbench/api/browser/mainThreadWebview'; +import type { MainThreadWebviewPanelsAndViews } from 'vs/workbench/api/browser/mainThreadWebviewPanelsAndViews'; +import { MainThreadWebviews } from 'vs/workbench/api/browser/mainThreadWebviews'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { editorGroupToViewColumn } from 'vs/workbench/api/common/shared/editor'; import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; @@ -22,6 +23,7 @@ export class MainThreadWebviewSerializers extends Disposable { constructor( private readonly mainThreadWebviews: MainThreadWebviews, + private readonly mainThreadWebviewsPanelsAndViews: MainThreadWebviewPanelsAndViews, context: extHostProtocol.IExtHostContext, @IExtensionService extensionService: IExtensionService, @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, @@ -40,7 +42,7 @@ export class MainThreadWebviewSerializers extends Disposable { return false; } - const viewType = this.mainThreadWebviews.webviewPanelViewType.toExternal(webview.viewType); + const viewType = this.mainThreadWebviewsPanelsAndViews.webviewPanelViewType.toExternal(webview.viewType); if (typeof viewType === 'string') { extensionService.activateByEvent(`onWebviewPanel:${viewType}`); } @@ -50,17 +52,18 @@ export class MainThreadWebviewSerializers extends Disposable { })); } - public $registerSerializer(viewType: string): void { + public $registerSerializer(viewType: string) + : void { if (this._revivers.has(viewType)) { throw new Error(`Reviver for ${viewType} already registered`); } this._revivers.set(viewType, this._webviewWorkbenchService.registerResolver({ canResolve: (webviewInput) => { - return webviewInput.viewType === this.mainThreadWebviews.webviewPanelViewType.fromExternal(viewType); + return webviewInput.viewType === this.mainThreadWebviewsPanelsAndViews.webviewPanelViewType.fromExternal(viewType); }, resolveWebview: async (webviewInput): Promise => { - const viewType = this.mainThreadWebviews.webviewPanelViewType.toExternal(webviewInput.viewType); + const viewType = this.mainThreadWebviewsPanelsAndViews.webviewPanelViewType.toExternal(webviewInput.viewType); if (!viewType) { webviewInput.webview.html = this.mainThreadWebviews.getWebviewResolvedFailedContent(webviewInput.viewType); return; @@ -69,7 +72,7 @@ export class MainThreadWebviewSerializers extends Disposable { const handle = webviewInput.id; - this.mainThreadWebviews.addWebviewInput(handle, webviewInput); + this.mainThreadWebviewsPanelsAndViews.addWebviewInput(handle, webviewInput); let state = undefined; if (webviewInput.webview.state) { diff --git a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts index 2d223830a0b..723de108e3d 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts @@ -6,12 +6,12 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import type { MainThreadWebviews } from 'vs/workbench/api/browser/mainThreadWebview'; +import { MainThreadWebviews } from 'vs/workbench/api/browser/mainThreadWebviews'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { IWebviewViewService, WebviewView } from 'vs/workbench/contrib/webviewView/browser/webviewViewService'; -export class MainThreadWebviewsViews extends Disposable { +export class MainThreadWebviewsViews extends Disposable implements extHostProtocol.MainThreadWebviewViewsShape { private readonly _proxyViews: extHostProtocol.ExtHostWebviewViewsShape; diff --git a/src/vs/workbench/api/browser/mainThreadWebviews.ts b/src/vs/workbench/api/browser/mainThreadWebviews.ts new file mode 100644 index 00000000000..4f9bc90fe91 --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadWebviews.ts @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; +import { isWeb } from 'vs/base/common/platform'; +import { escape } from 'vs/base/common/strings'; +import { URI } from 'vs/base/common/uri'; +import { IWebviewOptions } from 'vs/editor/common/modes'; +import { localize } from 'vs/nls'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IProductService } from 'vs/platform/product/common/productService'; +import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; +import { Webview, WebviewExtensionDescription, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; +import { WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; + +export class MainThreadWebviews extends Disposable implements extHostProtocol.MainThreadWebviewsShape { + + private static readonly standardSupportedLinkSchemes = new Set([ + Schemas.http, + Schemas.https, + Schemas.mailto, + Schemas.vscode, + 'vscode-insider', + ]); + + private readonly _proxy: extHostProtocol.ExtHostWebviewsShape; + + private readonly _webviews = new Map(); + + constructor( + context: extHostProtocol.IExtHostContext, + @IOpenerService private readonly _openerService: IOpenerService, + @IProductService private readonly _productService: IProductService, + ) { + super(); + + this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviews); + } + + public addWebview(handle: extHostProtocol.WebviewPanelHandle, webview: WebviewOverlay): void { + this._webviews.set(handle, webview); + this.hookupWebviewEventDelegate(handle, webview); + } + + public $setHtml(handle: extHostProtocol.WebviewPanelHandle, value: string): void { + const webview = this.getWebview(handle); + webview.html = value; + } + + public $setOptions(handle: extHostProtocol.WebviewPanelHandle, options: IWebviewOptions): void { + const webview = this.getWebview(handle); + webview.contentOptions = reviveWebviewOptions(options); + } + + public async $postMessage(handle: extHostProtocol.WebviewPanelHandle, message: any): Promise { + const webview = this.getWebview(handle); + webview.postMessage(message); + return true; + } + + private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewPanelHandle, webview: WebviewOverlay) { + const disposables = new DisposableStore(); + + disposables.add(webview.onDidClickLink((uri) => this.onDidClickLink(handle, uri))); + disposables.add(webview.onMessage((message: any) => { this._proxy.$onMessage(handle, message); })); + disposables.add(webview.onMissingCsp((extension: ExtensionIdentifier) => this._proxy.$onMissingCsp(handle, extension.value))); + + disposables.add(webview.onDispose(() => { + disposables.dispose(); + this._webviews.delete(handle); + })); + } + + private onDidClickLink(handle: extHostProtocol.WebviewPanelHandle, link: string): void { + const webview = this.getWebview(handle); + if (this.isSupportedLink(webview, URI.parse(link))) { + this._openerService.open(link, { fromUserGesture: true }); + } + } + + private isSupportedLink(webview: Webview, link: URI): boolean { + if (MainThreadWebviews.standardSupportedLinkSchemes.has(link.scheme)) { + return true; + } + if (!isWeb && this._productService.urlProtocol === link.scheme) { + return true; + } + return !!webview.contentOptions.enableCommandUris && link.scheme === Schemas.command; + } + + private getWebview(handle: extHostProtocol.WebviewPanelHandle): Webview { + const webview = this._webviews.get(handle); + if (!webview) { + throw new Error(`Unknown webview handle:${handle}`); + } + return webview; + } + + public getWebviewResolvedFailedContent(viewType: string) { + return ` + + + + + + ${localize('errorMessage', "An error occurred while loading view: {0}", escape(viewType))} + `; + } +} + +export function reviveWebviewExtension(extensionData: extHostProtocol.WebviewExtensionDescription): WebviewExtensionDescription { + return { id: extensionData.id, location: URI.revive(extensionData.location) }; +} + +export function reviveWebviewOptions(options: IWebviewOptions): WebviewInputOptions { + return { + ...options, + allowScripts: options.enableScripts, + localResourceRoots: Array.isArray(options.localResourceRoots) ? options.localResourceRoots.map(r => URI.revive(r)) : undefined, + }; +} diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index ad16212e4d0..a1b8f526197 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -609,27 +609,32 @@ export interface CustomTextEditorCapabilities { } export interface MainThreadWebviewsShape extends IDisposable { + $setHtml(handle: WebviewPanelHandle, value: string): void; + $setOptions(handle: WebviewPanelHandle, options: modes.IWebviewOptions): void; + $postMessage(handle: WebviewPanelHandle, value: any): Promise +} + +export interface MainThreadWebviewPanelsAndViewsShape extends IDisposable { $createWebviewPanel(extension: WebviewExtensionDescription, handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: modes.IWebviewPanelOptions & modes.IWebviewOptions): void; $disposeWebview(handle: WebviewPanelHandle): void; $reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void; $setTitle(handle: WebviewPanelHandle, value: string): void; $setIconPath(handle: WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents; } | undefined): void; - $setHtml(handle: WebviewPanelHandle, value: string): void; - $setOptions(handle: WebviewPanelHandle, options: modes.IWebviewOptions): void; - - $postMessage(handle: WebviewPanelHandle, value: any): Promise; - $registerSerializer(viewType: string): void; $unregisterSerializer(viewType: string): void; +} +export interface MainThreadCustomEditorsShape extends IDisposable { $registerTextEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: CustomTextEditorCapabilities): void; $registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void; $unregisterEditorProvider(viewType: string): void; $onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void; $onContentChange(resource: UriComponents, viewType: string): void; +} +export interface MainThreadWebviewViewsShape extends IDisposable { $registerWebviewViewProvider(viewType: string, options?: { retainContextWhenHidden?: boolean }): void; $unregisterWebviewViewProvider(viewType: string): void; @@ -1715,7 +1720,10 @@ export const MainContext = { MainThreadStorage: createMainId('MainThreadStorage'), MainThreadTelemetry: createMainId('MainThreadTelemetry'), MainThreadTerminalService: createMainId('MainThreadTerminalService'), + MainThreadWebviewService: createMainId('MainThreadWebviewService'), MainThreadWebviews: createMainId('MainThreadWebviews'), + MainThreadWebviewViews: createMainId('MainThreadWebviewViews'), + MainThreadCustomEditors: createMainId('MainThreadCustomEditors'), MainThreadUrls: createMainId('MainThreadUrls'), MainThreadWorkspace: createMainId('MainThreadWorkspace'), MainThreadFileSystem: createMainId('MainThreadFileSystem'), diff --git a/src/vs/workbench/api/common/extHostCustomEditors.ts b/src/vs/workbench/api/common/extHostCustomEditors.ts index f8275fab414..285c9aaea17 100644 --- a/src/vs/workbench/api/common/extHostCustomEditors.ts +++ b/src/vs/workbench/api/common/extHostCustomEditors.ts @@ -154,7 +154,7 @@ class EditorProviderStore { export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditorsShape { - private readonly _proxy: extHostProtocol.MainThreadWebviewsShape; + private readonly _proxy: extHostProtocol.MainThreadCustomEditorsShape; private readonly _editorProviders = new EditorProviderStore(); @@ -166,7 +166,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor private readonly _extensionStoragePaths: IExtensionStoragePaths | undefined, private readonly _extHostWebview: ExtHostWebviews, ) { - this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadWebviews); + this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadCustomEditors); } public registerCustomEditorProvider( diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index 1c9c1ac5741..4446bfacff1 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -120,7 +120,7 @@ type IconPath = URI | { light: URI, dark: URI }; class ExtHostWebviewPanel extends Disposable implements vscode.WebviewPanel { readonly #handle: extHostProtocol.WebviewPanelHandle; - readonly #proxy: extHostProtocol.MainThreadWebviewsShape; + readonly #proxy: extHostProtocol.MainThreadWebviewPanelsAndViewsShape; readonly #viewType: string; readonly #webview: ExtHostWebview; @@ -141,7 +141,7 @@ class ExtHostWebviewPanel extends Disposable implements vscode.WebviewPanel { constructor( handle: extHostProtocol.WebviewPanelHandle, - proxy: extHostProtocol.MainThreadWebviewsShape, + proxy: extHostProtocol.MainThreadWebviewPanelsAndViewsShape, viewType: string, title: string, viewColumn: vscode.ViewColumn | undefined, @@ -246,11 +246,6 @@ class ExtHostWebviewPanel extends Disposable implements vscode.WebviewPanel { } } - public postMessage(message: any): Promise { - this.assertNotDisposed(); - return this.#proxy.$postMessage(this.#handle, message); - } - public reveal(viewColumn?: vscode.ViewColumn, preserveFocus?: boolean): void { this.assertNotDisposed(); this.#proxy.$reveal(this.#handle, { @@ -272,12 +267,12 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { return generateUuid(); } - private readonly _proxy: extHostProtocol.MainThreadWebviewsShape; + private readonly _proxy: extHostProtocol.MainThreadWebviewPanelsAndViewsShape; + private readonly _webviewProxy: extHostProtocol.MainThreadWebviewsShape; private readonly _webviews = new Map(); private readonly _webviewPanels = new Map(); - constructor( mainContext: extHostProtocol.IMainContext, private readonly initData: WebviewInitData, @@ -285,7 +280,8 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { private readonly _logService: ILogService, private readonly _deprecationService: IExtHostApiDeprecationService, ) { - this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadWebviews); + this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadWebviewService); + this._webviewProxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadWebviews); } public createWebviewPanel( @@ -375,7 +371,7 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { } public createNewWebview(handle: string, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, extension: IExtensionDescription): ExtHostWebview { - const webview = new ExtHostWebview(handle, this._proxy, reviveOptions(options), this.initData, this.workspace, extension, this._deprecationService); + const webview = new ExtHostWebview(handle, this._webviewProxy, reviveOptions(options), this.initData, this.workspace, extension, this._deprecationService); this._webviews.set(handle, webview); webview._onDidDispose(() => { this._webviews.delete(handle); }); diff --git a/src/vs/workbench/api/common/extHostWebviewSerializer.ts b/src/vs/workbench/api/common/extHostWebviewSerializer.ts index 274f91f5353..4cef8052ea6 100644 --- a/src/vs/workbench/api/common/extHostWebviewSerializer.ts +++ b/src/vs/workbench/api/common/extHostWebviewSerializer.ts @@ -13,7 +13,7 @@ import * as extHostTypes from './extHostTypes'; export class ExtHostWebviewSerializer implements extHostProtocol.ExtHostWebviewSerializerShape { - private readonly _proxy: extHostProtocol.MainThreadWebviewsShape; + private readonly _proxy: extHostProtocol.MainThreadWebviewPanelsAndViewsShape; private readonly _serializers = new Map { function createNoopMainThreadWebviews() { - return new class extends mock() { + return new class extends mock() { $createWebviewPanel() { /* noop */ } $registerSerializer() { /* noop */ } $unregisterSerializer() { /* noop */ } From 51ba70d426cfbf88d91932c3117f40d0f9bf930c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 26 Aug 2020 14:47:51 -0700 Subject: [PATCH 575/736] Undo split of serializers into own main thread class The serializers belong with the panels, not in their own class --- .../mainThreadWebviewPanelsAndViews.ts | 80 +++++++++++-- .../browser/mainThreadWebviewSerializer.ts | 105 ------------------ .../workbench/api/common/extHost.api.impl.ts | 2 +- .../workbench/api/common/extHost.protocol.ts | 4 +- .../api/common/extHostWebviewSerializer.ts | 2 +- 5 files changed, 76 insertions(+), 117 deletions(-) delete mode 100644 src/vs/workbench/api/browser/mainThreadWebviewSerializer.ts diff --git a/src/vs/workbench/api/browser/mainThreadWebviewPanelsAndViews.ts b/src/vs/workbench/api/browser/mainThreadWebviewPanelsAndViews.ts index d019b03336c..3a0fb3017ab 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewPanelsAndViews.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewPanelsAndViews.ts @@ -3,23 +3,25 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { onUnexpectedError } from 'vs/base/common/errors'; import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { MainThreadCustomEditors } from 'vs/workbench/api/browser/mainThreadCustomEditors'; import { MainThreadWebviews, reviveWebviewExtension, reviveWebviewOptions } from 'vs/workbench/api/browser/mainThreadWebviews'; -import { MainThreadWebviewSerializers } from 'vs/workbench/api/browser/mainThreadWebviewSerializer'; import { MainThreadWebviewsViews } from 'vs/workbench/api/browser/mainThreadWebviewViews'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; import { IEditorInput } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; +import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; import { WebviewIcons } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; import { ICreateWebViewShowOptions, IWebviewWorkbenchService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { extHostNamedCustomer } from '../common/extHostCustomers'; /** @@ -78,9 +80,10 @@ class WebviewViewTypeTransformer { @extHostNamedCustomer(extHostProtocol.MainContext.MainThreadWebviewService) export class MainThreadWebviewPanelsAndViews extends Disposable implements extHostProtocol.MainThreadWebviewPanelsAndViewsShape { - public readonly webviewPanelViewType = new WebviewViewTypeTransformer('mainThreadWebview-'); + private readonly webviewPanelViewType = new WebviewViewTypeTransformer('mainThreadWebview-'); private readonly _proxy: extHostProtocol.ExtHostWebviewsShape; + private readonly _proxyPanels: extHostProtocol.ExtHostWebviewPanelsShape; private readonly _webviewInputs = new WebviewInputStore(); @@ -89,10 +92,11 @@ export class MainThreadWebviewPanelsAndViews extends Disposable implements extHo private readonly _mainThreadWebviews: MainThreadWebviews; - private readonly serializers: MainThreadWebviewSerializers; + private readonly _revivers = new Map(); constructor( context: extHostProtocol.IExtHostContext, + @IExtensionService extensionService: IExtensionService, @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, @IEditorService private readonly _editorService: IEditorService, @ITelemetryService private readonly _telemetryService: ITelemetryService, @@ -102,6 +106,7 @@ export class MainThreadWebviewPanelsAndViews extends Disposable implements extHo super(); this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviews); + this._proxyPanels = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewPanels); this._mainThreadWebviews = this._instantiationService.createInstance(MainThreadWebviews, context); context.set(extHostProtocol.MainContext.MainThreadWebviews, this._mainThreadWebviews); @@ -112,8 +117,6 @@ export class MainThreadWebviewPanelsAndViews extends Disposable implements extHo const customEditors = this._instantiationService.createInstance(MainThreadCustomEditors, this._mainThreadWebviews, this, context); context.set(extHostProtocol.MainContext.MainThreadCustomEditors, customEditors); - this.serializers = this._instantiationService.createInstance(MainThreadWebviewSerializers, this._mainThreadWebviews, this, context); - this._register(_editorService.onDidActiveEditorChange(() => { const activeInput = this._editorService.activeEditor; if (activeInput instanceof DiffEditorInput && activeInput.primary instanceof WebviewInput && activeInput.secondary instanceof WebviewInput) { @@ -126,6 +129,24 @@ export class MainThreadWebviewPanelsAndViews extends Disposable implements extHo this._register(_editorService.onDidVisibleEditorsChange(() => { this.updateWebviewViewStates(this._editorService.activeEditor); })); + + // This reviver's only job is to activate extensions. + // This should trigger the real reviver to be registered from the extension host side. + this._register(_webviewWorkbenchService.registerResolver({ + canResolve: (webview: WebviewInput) => { + if (webview instanceof CustomEditorInput) { + extensionService.activateByEvent(`onCustomEditor:${webview.viewType}`); + return false; + } + + const viewType = this.webviewPanelViewType.toExternal(webview.viewType); + if (typeof viewType === 'string') { + extensionService.activateByEvent(`onWebviewPanel:${viewType}`); + } + return false; + }, + resolveWebview: () => { throw new Error('not implemented'); } + })); } dispose() { @@ -205,12 +226,55 @@ export class MainThreadWebviewPanelsAndViews extends Disposable implements extHo } } - public $registerSerializer(viewType: string): void { - this.serializers.$registerSerializer(viewType); + public $registerSerializer(viewType: string) + : void { + if (this._revivers.has(viewType)) { + throw new Error(`Reviver for ${viewType} already registered`); + } + + this._revivers.set(viewType, this._webviewWorkbenchService.registerResolver({ + canResolve: (webviewInput) => { + return webviewInput.viewType === this.webviewPanelViewType.fromExternal(viewType); + }, + resolveWebview: async (webviewInput): Promise => { + const viewType = this.webviewPanelViewType.toExternal(webviewInput.viewType); + if (!viewType) { + webviewInput.webview.html = this._mainThreadWebviews.getWebviewResolvedFailedContent(webviewInput.viewType); + return; + } + + + const handle = webviewInput.id; + + this.addWebviewInput(handle, webviewInput); + + let state = undefined; + if (webviewInput.webview.state) { + try { + state = JSON.parse(webviewInput.webview.state); + } catch (e) { + console.error('Could not load webview state', e, webviewInput.webview.state); + } + } + + try { + await this._proxyPanels.$deserializeWebviewPanel(handle, viewType, webviewInput.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options); + } catch (error) { + onUnexpectedError(error); + webviewInput.webview.html = this._mainThreadWebviews.getWebviewResolvedFailedContent(viewType); + } + } + })); } public $unregisterSerializer(viewType: string): void { - this.serializers.$unregisterSerializer(viewType); + const reviver = this._revivers.get(viewType); + if (!reviver) { + throw new Error(`No reviver for ${viewType} registered`); + } + + reviver.dispose(); + this._revivers.delete(viewType); } private registerWebviewFromDiffEditorListeners(diffEditorInput: DiffEditorInput): void { diff --git a/src/vs/workbench/api/browser/mainThreadWebviewSerializer.ts b/src/vs/workbench/api/browser/mainThreadWebviewSerializer.ts deleted file mode 100644 index ee585c7298d..00000000000 --- a/src/vs/workbench/api/browser/mainThreadWebviewSerializer.ts +++ /dev/null @@ -1,105 +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 { onUnexpectedError } from 'vs/base/common/errors'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import type { MainThreadWebviewPanelsAndViews } from 'vs/workbench/api/browser/mainThreadWebviewPanelsAndViews'; -import { MainThreadWebviews } from 'vs/workbench/api/browser/mainThreadWebviews'; -import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; -import { editorGroupToViewColumn } from 'vs/workbench/api/common/shared/editor'; -import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; -import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; -import { IWebviewWorkbenchService } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; -import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; - -export class MainThreadWebviewSerializers extends Disposable { - - private readonly _proxy: extHostProtocol.ExtHostWebviewSerializerShape; - - private readonly _revivers = new Map(); - - constructor( - private readonly mainThreadWebviews: MainThreadWebviews, - private readonly mainThreadWebviewsPanelsAndViews: MainThreadWebviewPanelsAndViews, - context: extHostProtocol.IExtHostContext, - @IExtensionService extensionService: IExtensionService, - @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, - @IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService, - ) { - super(); - - this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewSerializer); - - // This reviver's only job is to activate extensions. - // This should trigger the real reviver to be registered from the extension host side. - this._register(_webviewWorkbenchService.registerResolver({ - canResolve: (webview: WebviewInput) => { - if (webview instanceof CustomEditorInput) { - extensionService.activateByEvent(`onCustomEditor:${webview.viewType}`); - return false; - } - - const viewType = this.mainThreadWebviewsPanelsAndViews.webviewPanelViewType.toExternal(webview.viewType); - if (typeof viewType === 'string') { - extensionService.activateByEvent(`onWebviewPanel:${viewType}`); - } - return false; - }, - resolveWebview: () => { throw new Error('not implemented'); } - })); - } - - public $registerSerializer(viewType: string) - : void { - if (this._revivers.has(viewType)) { - throw new Error(`Reviver for ${viewType} already registered`); - } - - this._revivers.set(viewType, this._webviewWorkbenchService.registerResolver({ - canResolve: (webviewInput) => { - return webviewInput.viewType === this.mainThreadWebviewsPanelsAndViews.webviewPanelViewType.fromExternal(viewType); - }, - resolveWebview: async (webviewInput): Promise => { - const viewType = this.mainThreadWebviewsPanelsAndViews.webviewPanelViewType.toExternal(webviewInput.viewType); - if (!viewType) { - webviewInput.webview.html = this.mainThreadWebviews.getWebviewResolvedFailedContent(webviewInput.viewType); - return; - } - - - const handle = webviewInput.id; - - this.mainThreadWebviewsPanelsAndViews.addWebviewInput(handle, webviewInput); - - let state = undefined; - if (webviewInput.webview.state) { - try { - state = JSON.parse(webviewInput.webview.state); - } catch (e) { - console.error('Could not load webview state', e, webviewInput.webview.state); - } - } - - try { - await this._proxy.$deserializeWebviewPanel(handle, viewType, webviewInput.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options); - } catch (error) { - onUnexpectedError(error); - webviewInput.webview.html = this.mainThreadWebviews.getWebviewResolvedFailedContent(viewType); - } - } - })); - } - - public $unregisterSerializer(viewType: string): void { - const reviver = this._revivers.get(viewType); - if (!reviver) { - throw new Error(`No reviver for ${viewType} registered`); - } - - reviver.dispose(); - this._revivers.delete(viewType); - } -} diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index ea6cfe93bfe..9d75330130f 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -145,7 +145,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol)); const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol, extHostCommands)); const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace, extHostLogService, extHostApiDeprecation)); - const extHostWebviewSerializers = rpcProtocol.set(ExtHostContext.ExtHostWebviewSerializer, new ExtHostWebviewSerializer(rpcProtocol, extHostWebviews)); + const extHostWebviewSerializers = rpcProtocol.set(ExtHostContext.ExtHostWebviewPanels, new ExtHostWebviewSerializer(rpcProtocol, extHostWebviews)); const extHostCustomEditors = rpcProtocol.set(ExtHostContext.ExtHostCustomEditors, new ExtHostCustomEditors(rpcProtocol, extHostDocuments, extensionStoragePaths, extHostWebviews)); const extHostWebviewViews = rpcProtocol.set(ExtHostContext.ExtHostWebviewViews, new ExtHostWebviewViews(rpcProtocol, extHostWebviews)); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index a1b8f526197..29489f1e9a7 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -656,7 +656,7 @@ export interface ExtHostWebviewsShape { $onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise; } -export interface ExtHostWebviewSerializerShape { +export interface ExtHostWebviewPanelsShape { $deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; } @@ -1764,7 +1764,7 @@ export const ExtHostContext = { ExtHostWorkspace: createExtId('ExtHostWorkspace'), ExtHostWindow: createExtId('ExtHostWindow'), ExtHostWebviews: createExtId('ExtHostWebviews'), - ExtHostWebviewSerializer: createExtId('ExtHostWebviewSerializer'), + ExtHostWebviewPanels: createExtId('ExtHostWebviewPanels'), ExtHostCustomEditors: createExtId('ExtHostCustomEditors'), ExtHostWebviewViews: createExtId('ExtHostWebviewViews'), ExtHostEditorInsets: createExtId('ExtHostEditorInsets'), diff --git a/src/vs/workbench/api/common/extHostWebviewSerializer.ts b/src/vs/workbench/api/common/extHostWebviewSerializer.ts index 4cef8052ea6..51b413fbca6 100644 --- a/src/vs/workbench/api/common/extHostWebviewSerializer.ts +++ b/src/vs/workbench/api/common/extHostWebviewSerializer.ts @@ -11,7 +11,7 @@ import type * as vscode from 'vscode'; import * as extHostProtocol from './extHost.protocol'; import * as extHostTypes from './extHostTypes'; -export class ExtHostWebviewSerializer implements extHostProtocol.ExtHostWebviewSerializerShape { +export class ExtHostWebviewSerializer implements extHostProtocol.ExtHostWebviewPanelsShape { private readonly _proxy: extHostProtocol.MainThreadWebviewPanelsAndViewsShape; From 79d7a9569da700dde96b4e652d7614f0328a8535 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 26 Aug 2020 15:01:18 -0700 Subject: [PATCH 576/736] Undoing exthost side of the webview serializer split --- .../mainThreadWebviewPanelsAndViews.ts | 8 +-- .../workbench/api/common/extHost.api.impl.ts | 5 +- .../workbench/api/common/extHost.protocol.ts | 4 +- src/vs/workbench/api/common/extHostWebview.ts | 47 ++++++++++++- .../api/common/extHostWebviewSerializer.ts | 66 ------------------- .../test/browser/api/extHostWebview.test.ts | 13 ++-- 6 files changed, 58 insertions(+), 85 deletions(-) delete mode 100644 src/vs/workbench/api/common/extHostWebviewSerializer.ts diff --git a/src/vs/workbench/api/browser/mainThreadWebviewPanelsAndViews.ts b/src/vs/workbench/api/browser/mainThreadWebviewPanelsAndViews.ts index 3a0fb3017ab..5d1e69ffcb0 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewPanelsAndViews.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewPanelsAndViews.ts @@ -82,8 +82,7 @@ export class MainThreadWebviewPanelsAndViews extends Disposable implements extHo private readonly webviewPanelViewType = new WebviewViewTypeTransformer('mainThreadWebview-'); - private readonly _proxy: extHostProtocol.ExtHostWebviewsShape; - private readonly _proxyPanels: extHostProtocol.ExtHostWebviewPanelsShape; + private readonly _proxy: extHostProtocol.ExtHostWebviewPanelsShape; private readonly _webviewInputs = new WebviewInputStore(); @@ -105,8 +104,7 @@ export class MainThreadWebviewPanelsAndViews extends Disposable implements extHo ) { super(); - this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviews); - this._proxyPanels = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewPanels); + this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewPanels); this._mainThreadWebviews = this._instantiationService.createInstance(MainThreadWebviews, context); context.set(extHostProtocol.MainContext.MainThreadWebviews, this._mainThreadWebviews); @@ -258,7 +256,7 @@ export class MainThreadWebviewPanelsAndViews extends Disposable implements extHo } try { - await this._proxyPanels.$deserializeWebviewPanel(handle, viewType, webviewInput.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options); + await this._proxy.$deserializeWebviewPanel(handle, viewType, webviewInput.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options); } catch (error) { onUnexpectedError(error); webviewInput.webview.html = this._mainThreadWebviews.getWebviewResolvedFailedContent(viewType); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 9d75330130f..0785eb62371 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -78,7 +78,6 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer'; import { ExtHostWebviewViews } from 'vs/workbench/api/common/extHostWebviewView'; import { ExtHostCustomEditors } from 'vs/workbench/api/common/extHostCustomEditors'; -import { ExtHostWebviewSerializer } from 'vs/workbench/api/common/extHostWebviewSerializer'; export interface IExtensionApiFactory { (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; @@ -145,7 +144,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol)); const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol, extHostCommands)); const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace, extHostLogService, extHostApiDeprecation)); - const extHostWebviewSerializers = rpcProtocol.set(ExtHostContext.ExtHostWebviewPanels, new ExtHostWebviewSerializer(rpcProtocol, extHostWebviews)); + rpcProtocol.set(ExtHostContext.ExtHostWebviewPanels, extHostWebviews); const extHostCustomEditors = rpcProtocol.set(ExtHostContext.ExtHostCustomEditors, new ExtHostCustomEditors(rpcProtocol, extHostDocuments, extensionStoragePaths, extHostWebviews)); const extHostWebviewViews = rpcProtocol.set(ExtHostContext.ExtHostWebviewViews, new ExtHostWebviewViews(rpcProtocol, extHostWebviews)); @@ -595,7 +594,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostTreeViews.createTreeView(viewId, options, extension); }, registerWebviewPanelSerializer: (viewType: string, serializer: vscode.WebviewPanelSerializer) => { - return extHostWebviewSerializers.registerWebviewPanelSerializer(extension, viewType, serializer); + return extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializer); }, registerCustomEditorProvider: (viewType: string, provider: vscode.CustomTextEditorProvider | vscode.CustomReadonlyEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerDocument?: boolean } = {}) => { return extHostCustomEditors.registerCustomEditorProvider(extension, viewType, provider, options); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 29489f1e9a7..4ec9a3af1f5 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -652,11 +652,11 @@ export interface WebviewPanelViewStateData { export interface ExtHostWebviewsShape { $onMessage(handle: WebviewPanelHandle, message: any): void; $onMissingCsp(handle: WebviewPanelHandle, extensionId: string): void; - $onDidChangeWebviewPanelViewStates(newState: WebviewPanelViewStateData): void; - $onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise; } export interface ExtHostWebviewPanelsShape { + $onDidChangeWebviewPanelViewStates(newState: WebviewPanelViewStateData): void; + $onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise; $deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; } diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index 4446bfacff1..60486b693c2 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -13,9 +13,11 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; +import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; import type * as vscode from 'vscode'; import * as extHostProtocol from './extHost.protocol'; +import * as extHostTypes from './extHostTypes'; export class ExtHostWebview implements vscode.Webview { @@ -261,7 +263,7 @@ class ExtHostWebviewPanel extends Disposable implements vscode.WebviewPanel { } } -export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { +export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape, extHostProtocol.ExtHostWebviewPanelsShape { private static newHandle(): extHostProtocol.WebviewPanelHandle { return generateUuid(); @@ -273,6 +275,11 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { private readonly _webviews = new Map(); private readonly _webviewPanels = new Map(); + private readonly _serializers = new Map(); + constructor( mainContext: extHostProtocol.IMainContext, private readonly initData: WebviewInitData, @@ -364,6 +371,44 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { this._webviews.delete(handle); } + + public registerWebviewPanelSerializer( + extension: IExtensionDescription, + viewType: string, + serializer: vscode.WebviewPanelSerializer + ): vscode.Disposable { + if (this._serializers.has(viewType)) { + throw new Error(`Serializer for '${viewType}' already registered`); + } + + this._serializers.set(viewType, { serializer, extension }); + this._proxy.$registerSerializer(viewType); + + return new extHostTypes.Disposable(() => { + this._serializers.delete(viewType); + this._proxy.$unregisterSerializer(viewType); + }); + } + + async $deserializeWebviewPanel( + webviewHandle: extHostProtocol.WebviewPanelHandle, + viewType: string, + title: string, + state: any, + position: EditorViewColumn, + options: modes.IWebviewOptions & modes.IWebviewPanelOptions + ): Promise { + const entry = this._serializers.get(viewType); + if (!entry) { + throw new Error(`No serializer found for '${viewType}'`); + } + const { serializer, extension } = entry; + + const webview = this.createNewWebview(webviewHandle, options, extension); + const revivedPanel = this.createNewWebviewPanel(webviewHandle, viewType, title, position, options, webview); + await serializer.deserializeWebviewPanel(revivedPanel, state); + } + public createNewWebviewPanel(webviewHandle: string, viewType: string, title: string, position: number, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, webview: ExtHostWebview) { const panel = new ExtHostWebviewPanel(webviewHandle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview); this._webviewPanels.set(webviewHandle, panel); diff --git a/src/vs/workbench/api/common/extHostWebviewSerializer.ts b/src/vs/workbench/api/common/extHostWebviewSerializer.ts deleted file mode 100644 index 51b413fbca6..00000000000 --- a/src/vs/workbench/api/common/extHostWebviewSerializer.ts +++ /dev/null @@ -1,66 +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 modes from 'vs/editor/common/modes'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview'; -import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; -import type * as vscode from 'vscode'; -import * as extHostProtocol from './extHost.protocol'; -import * as extHostTypes from './extHostTypes'; - -export class ExtHostWebviewSerializer implements extHostProtocol.ExtHostWebviewPanelsShape { - - private readonly _proxy: extHostProtocol.MainThreadWebviewPanelsAndViewsShape; - - private readonly _serializers = new Map(); - - constructor( - mainContext: extHostProtocol.IMainContext, - private readonly _webviewService: ExtHostWebviews, - ) { - this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadWebviewService); - } - - public registerWebviewPanelSerializer( - extension: IExtensionDescription, - viewType: string, - serializer: vscode.WebviewPanelSerializer - ): vscode.Disposable { - if (this._serializers.has(viewType)) { - throw new Error(`Serializer for '${viewType}' already registered`); - } - - this._serializers.set(viewType, { serializer, extension }); - this._proxy.$registerSerializer(viewType); - - return new extHostTypes.Disposable(() => { - this._serializers.delete(viewType); - this._proxy.$unregisterSerializer(viewType); - }); - } - - async $deserializeWebviewPanel( - webviewHandle: extHostProtocol.WebviewPanelHandle, - viewType: string, - title: string, - state: any, - position: EditorViewColumn, - options: modes.IWebviewOptions & modes.IWebviewPanelOptions - ): Promise { - const entry = this._serializers.get(viewType); - if (!entry) { - throw new Error(`No serializer found for '${viewType}'`); - } - const { serializer, extension } = entry; - - const webview = this._webviewService.createNewWebview(webviewHandle, options, extension); - const revivedPanel = this._webviewService.createNewWebviewPanel(webviewHandle, viewType, title, position, options, webview); - await serializer.deserializeWebviewPanel(revivedPanel, state); - } -} diff --git a/src/vs/workbench/test/browser/api/extHostWebview.test.ts b/src/vs/workbench/test/browser/api/extHostWebview.test.ts index 6859a91c4d6..31e8bf7bacf 100644 --- a/src/vs/workbench/test/browser/api/extHostWebview.test.ts +++ b/src/vs/workbench/test/browser/api/extHostWebview.test.ts @@ -13,7 +13,6 @@ import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { NullApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview'; -import { ExtHostWebviewSerializer } from 'vs/workbench/api/common/extHostWebviewSerializer'; import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; import type * as vscode from 'vscode'; import { SingleProxyRPCProtocol } from './testRPCProtocol'; @@ -36,8 +35,6 @@ suite('ExtHostWebview', () => { isExtensionDevelopmentDebug: false, }, undefined, new NullLogService(), NullApiDeprecationService); - const extHostWebviewSerializer = new ExtHostWebviewSerializer(rpcProtocol!, extHostWebviews); - let lastInvokedDeserializer: vscode.WebviewPanelSerializer | undefined = undefined; class NoopSerializer implements vscode.WebviewPanelSerializer { @@ -51,20 +48,20 @@ suite('ExtHostWebview', () => { const serializerA = new NoopSerializer(); const serializerB = new NoopSerializer(); - const serializerARegistration = extHostWebviewSerializer.registerWebviewPanelSerializer(extension, viewType, serializerA); + const serializerARegistration = extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializerA); - await extHostWebviewSerializer.$deserializeWebviewPanel('x', viewType, 'title', {}, 0 as EditorViewColumn, {}); + await extHostWebviews.$deserializeWebviewPanel('x', viewType, 'title', {}, 0 as EditorViewColumn, {}); assert.strictEqual(lastInvokedDeserializer, serializerA); assert.throws( - () => extHostWebviewSerializer.registerWebviewPanelSerializer(extension, viewType, serializerB), + () => extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializerB), 'Should throw when registering two serializers for the same view'); serializerARegistration.dispose(); - extHostWebviewSerializer.registerWebviewPanelSerializer(extension, viewType, serializerB); + extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializerB); - await extHostWebviewSerializer.$deserializeWebviewPanel('x', viewType, 'title', {}, 0 as EditorViewColumn, {}); + await extHostWebviews.$deserializeWebviewPanel('x', viewType, 'title', {}, 0 as EditorViewColumn, {}); assert.strictEqual(lastInvokedDeserializer, serializerB); }); From 7e4ccf1676aa48d3eb84e4c69b35d5da84ef1209 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 26 Aug 2020 15:06:02 -0700 Subject: [PATCH 577/736] Rename WebviewPanelHandle -> WebviewHandle --- .../mainThreadWebviewPanelsAndViews.ts | 16 ++++---- .../api/browser/mainThreadWebviewViews.ts | 2 +- .../api/browser/mainThreadWebviews.ts | 14 +++---- .../workbench/api/common/extHost.protocol.ts | 38 +++++++++---------- .../api/common/extHostCustomEditors.ts | 2 +- src/vs/workbench/api/common/extHostWebview.ts | 26 ++++++------- .../api/common/extHostWebviewView.ts | 6 +-- 7 files changed, 52 insertions(+), 52 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadWebviewPanelsAndViews.ts b/src/vs/workbench/api/browser/mainThreadWebviewPanelsAndViews.ts index 5d1e69ffcb0..ea94413c303 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewPanelsAndViews.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewPanelsAndViews.ts @@ -158,7 +158,7 @@ export class MainThreadWebviewPanelsAndViews extends Disposable implements extHo public get webviewInputs(): Iterable { return this._webviewInputs; } - public addWebviewInput(handle: extHostProtocol.WebviewPanelHandle, input: WebviewInput): void { + public addWebviewInput(handle: extHostProtocol.WebviewHandle, input: WebviewInput): void { this._webviewInputs.add(handle, input); this._mainThreadWebviews.addWebview(handle, input.webview); @@ -171,7 +171,7 @@ export class MainThreadWebviewPanelsAndViews extends Disposable implements extHo public $createWebviewPanel( extensionData: extHostProtocol.WebviewExtensionDescription, - handle: extHostProtocol.WebviewPanelHandle, + handle: extHostProtocol.WebviewHandle, viewType: string, title: string, showOptions: { viewColumn?: EditorViewColumn, preserveFocus?: boolean; }, @@ -196,23 +196,23 @@ export class MainThreadWebviewPanelsAndViews extends Disposable implements extHo this._telemetryService.publicLog('webviews:createWebviewPanel', { extensionId: extension.id.value }); } - public $disposeWebview(handle: extHostProtocol.WebviewPanelHandle): void { + public $disposeWebview(handle: extHostProtocol.WebviewHandle): void { const webview = this.getWebviewInput(handle); webview.dispose(); } - public $setTitle(handle: extHostProtocol.WebviewPanelHandle, value: string): void { + public $setTitle(handle: extHostProtocol.WebviewHandle, value: string): void { const webview = this.getWebviewInput(handle); webview.setName(value); } - public $setIconPath(handle: extHostProtocol.WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents; } | undefined): void { + public $setIconPath(handle: extHostProtocol.WebviewHandle, value: { light: UriComponents, dark: UriComponents; } | undefined): void { const webview = this.getWebviewInput(handle); webview.iconPath = reviveWebviewIcon(value); } - public $reveal(handle: extHostProtocol.WebviewPanelHandle, showOptions: extHostProtocol.WebviewPanelShowOptions): void { + public $reveal(handle: extHostProtocol.WebviewHandle, showOptions: extHostProtocol.WebviewPanelShowOptions): void { const webview = this.getWebviewInput(handle); if (webview.isDisposed()) { return; @@ -336,7 +336,7 @@ export class MainThreadWebviewPanelsAndViews extends Disposable implements extHo } } - private getWebviewInput(handle: extHostProtocol.WebviewPanelHandle): WebviewInput { + private getWebviewInput(handle: extHostProtocol.WebviewHandle): WebviewInput { const webview = this.tryGetWebviewInput(handle); if (!webview) { throw new Error(`Unknown webview handle:${handle}`); @@ -344,7 +344,7 @@ export class MainThreadWebviewPanelsAndViews extends Disposable implements extHo return webview; } - private tryGetWebviewInput(handle: extHostProtocol.WebviewPanelHandle): WebviewInput | undefined { + private tryGetWebviewInput(handle: extHostProtocol.WebviewHandle): WebviewInput | undefined { return this._webviewInputs.getInputForHandle(handle); } } diff --git a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts index 723de108e3d..df46e3c6e09 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts @@ -28,7 +28,7 @@ export class MainThreadWebviewsViews extends Disposable implements extHostProtoc this._proxyViews = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewViews); } - public $setWebviewViewTitle(handle: extHostProtocol.WebviewPanelHandle, value: string | undefined): void { + public $setWebviewViewTitle(handle: extHostProtocol.WebviewHandle, value: string | undefined): void { const webviewView = this._webviewViews.get(handle); if (!webviewView) { throw new Error('unknown webview view'); diff --git a/src/vs/workbench/api/browser/mainThreadWebviews.ts b/src/vs/workbench/api/browser/mainThreadWebviews.ts index 4f9bc90fe91..8180b9972ee 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviews.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviews.ts @@ -41,28 +41,28 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviews); } - public addWebview(handle: extHostProtocol.WebviewPanelHandle, webview: WebviewOverlay): void { + public addWebview(handle: extHostProtocol.WebviewHandle, webview: WebviewOverlay): void { this._webviews.set(handle, webview); this.hookupWebviewEventDelegate(handle, webview); } - public $setHtml(handle: extHostProtocol.WebviewPanelHandle, value: string): void { + public $setHtml(handle: extHostProtocol.WebviewHandle, value: string): void { const webview = this.getWebview(handle); webview.html = value; } - public $setOptions(handle: extHostProtocol.WebviewPanelHandle, options: IWebviewOptions): void { + public $setOptions(handle: extHostProtocol.WebviewHandle, options: IWebviewOptions): void { const webview = this.getWebview(handle); webview.contentOptions = reviveWebviewOptions(options); } - public async $postMessage(handle: extHostProtocol.WebviewPanelHandle, message: any): Promise { + public async $postMessage(handle: extHostProtocol.WebviewHandle, message: any): Promise { const webview = this.getWebview(handle); webview.postMessage(message); return true; } - private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewPanelHandle, webview: WebviewOverlay) { + private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewHandle, webview: WebviewOverlay) { const disposables = new DisposableStore(); disposables.add(webview.onDidClickLink((uri) => this.onDidClickLink(handle, uri))); @@ -75,7 +75,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma })); } - private onDidClickLink(handle: extHostProtocol.WebviewPanelHandle, link: string): void { + private onDidClickLink(handle: extHostProtocol.WebviewHandle, link: string): void { const webview = this.getWebview(handle); if (this.isSupportedLink(webview, URI.parse(link))) { this._openerService.open(link, { fromUserGesture: true }); @@ -92,7 +92,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma return !!webview.contentOptions.enableCommandUris && link.scheme === Schemas.command; } - private getWebview(handle: extHostProtocol.WebviewPanelHandle): Webview { + private getWebview(handle: extHostProtocol.WebviewHandle): Webview { const webview = this._webviews.get(handle); if (!webview) { throw new Error(`Unknown webview handle:${handle}`); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 4ec9a3af1f5..10ad9f81831 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -581,7 +581,7 @@ export interface ExtHostEditorInsetsShape { $onDidReceiveMessage(handle: number, message: any): void; } -export type WebviewPanelHandle = string; +export type WebviewHandle = string; export interface WebviewPanelShowOptions { readonly viewColumn?: EditorViewColumn; @@ -609,17 +609,17 @@ export interface CustomTextEditorCapabilities { } export interface MainThreadWebviewsShape extends IDisposable { - $setHtml(handle: WebviewPanelHandle, value: string): void; - $setOptions(handle: WebviewPanelHandle, options: modes.IWebviewOptions): void; - $postMessage(handle: WebviewPanelHandle, value: any): Promise + $setHtml(handle: WebviewHandle, value: string): void; + $setOptions(handle: WebviewHandle, options: modes.IWebviewOptions): void; + $postMessage(handle: WebviewHandle, value: any): Promise } export interface MainThreadWebviewPanelsAndViewsShape extends IDisposable { - $createWebviewPanel(extension: WebviewExtensionDescription, handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: modes.IWebviewPanelOptions & modes.IWebviewOptions): void; - $disposeWebview(handle: WebviewPanelHandle): void; - $reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void; - $setTitle(handle: WebviewPanelHandle, value: string): void; - $setIconPath(handle: WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents; } | undefined): void; + $createWebviewPanel(extension: WebviewExtensionDescription, handle: WebviewHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: modes.IWebviewPanelOptions & modes.IWebviewOptions): void; + $disposeWebview(handle: WebviewHandle): void; + $reveal(handle: WebviewHandle, showOptions: WebviewPanelShowOptions): void; + $setTitle(handle: WebviewHandle, value: string): void; + $setIconPath(handle: WebviewHandle, value: { light: UriComponents, dark: UriComponents; } | undefined): void; $registerSerializer(viewType: string): void; $unregisterSerializer(viewType: string): void; @@ -638,7 +638,7 @@ export interface MainThreadWebviewViewsShape extends IDisposable { $registerWebviewViewProvider(viewType: string, options?: { retainContextWhenHidden?: boolean }): void; $unregisterWebviewViewProvider(viewType: string): void; - $setWebviewViewTitle(handle: WebviewPanelHandle, value: string | undefined): void; + $setWebviewViewTitle(handle: WebviewHandle, value: string | undefined): void; } export interface WebviewPanelViewStateData { @@ -650,18 +650,18 @@ export interface WebviewPanelViewStateData { } export interface ExtHostWebviewsShape { - $onMessage(handle: WebviewPanelHandle, message: any): void; - $onMissingCsp(handle: WebviewPanelHandle, extensionId: string): void; + $onMessage(handle: WebviewHandle, message: any): void; + $onMissingCsp(handle: WebviewHandle, extensionId: string): void; } export interface ExtHostWebviewPanelsShape { $onDidChangeWebviewPanelViewStates(newState: WebviewPanelViewStateData): void; - $onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise; - $deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; + $onDidDisposeWebviewPanel(handle: WebviewHandle): Promise; + $deserializeWebviewPanel(newWebviewHandle: WebviewHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; } export interface ExtHostCustomEditorsShape { - $resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, cancellation: CancellationToken): Promise; + $resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, cancellation: CancellationToken): Promise; $createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken): Promise<{ editable: boolean }>; $disposeCustomDocument(resource: UriComponents, viewType: string): Promise; @@ -675,15 +675,15 @@ export interface ExtHostCustomEditorsShape { $backup(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise; - $onMoveCustomEditor(handle: WebviewPanelHandle, newResource: UriComponents, viewType: string): Promise; + $onMoveCustomEditor(handle: WebviewHandle, newResource: UriComponents, viewType: string): Promise; } export interface ExtHostWebviewViewsShape { - $resolveWebviewView(webviewHandle: WebviewPanelHandle, viewType: string, state: any, cancellation: CancellationToken): Promise; + $resolveWebviewView(webviewHandle: WebviewHandle, viewType: string, state: any, cancellation: CancellationToken): Promise; - $onDidChangeWebviewViewVisibility(webviewHandle: WebviewPanelHandle, visible: boolean): void; + $onDidChangeWebviewViewVisibility(webviewHandle: WebviewHandle, visible: boolean): void; - $disposeWebviewView(webviewHandle: WebviewPanelHandle): void; + $disposeWebviewView(webviewHandle: WebviewHandle): void; } export enum CellKind { diff --git a/src/vs/workbench/api/common/extHostCustomEditors.ts b/src/vs/workbench/api/common/extHostCustomEditors.ts index 285c9aaea17..779075db693 100644 --- a/src/vs/workbench/api/common/extHostCustomEditors.ts +++ b/src/vs/workbench/api/common/extHostCustomEditors.ts @@ -247,7 +247,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor async $resolveWebviewEditor( resource: UriComponents, - handle: extHostProtocol.WebviewPanelHandle, + handle: extHostProtocol.WebviewHandle, viewType: string, title: string, position: EditorViewColumn, diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index 60486b693c2..a7836032cd9 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -21,7 +21,7 @@ import * as extHostTypes from './extHostTypes'; export class ExtHostWebview implements vscode.Webview { - readonly #handle: extHostProtocol.WebviewPanelHandle; + readonly #handle: extHostProtocol.WebviewHandle; readonly #proxy: extHostProtocol.MainThreadWebviewsShape; readonly #deprecationService: IExtHostApiDeprecationService; @@ -35,7 +35,7 @@ export class ExtHostWebview implements vscode.Webview { #hasCalledAsWebviewUri = false; constructor( - handle: extHostProtocol.WebviewPanelHandle, + handle: extHostProtocol.WebviewHandle, proxy: extHostProtocol.MainThreadWebviewsShape, options: vscode.WebviewOptions, initData: WebviewInitData, @@ -121,7 +121,7 @@ type IconPath = URI | { light: URI, dark: URI }; class ExtHostWebviewPanel extends Disposable implements vscode.WebviewPanel { - readonly #handle: extHostProtocol.WebviewPanelHandle; + readonly #handle: extHostProtocol.WebviewHandle; readonly #proxy: extHostProtocol.MainThreadWebviewPanelsAndViewsShape; readonly #viewType: string; @@ -142,7 +142,7 @@ class ExtHostWebviewPanel extends Disposable implements vscode.WebviewPanel { public readonly onDidChangeViewState = this.#onDidChangeViewState.event; constructor( - handle: extHostProtocol.WebviewPanelHandle, + handle: extHostProtocol.WebviewHandle, proxy: extHostProtocol.MainThreadWebviewPanelsAndViewsShape, viewType: string, title: string, @@ -265,15 +265,15 @@ class ExtHostWebviewPanel extends Disposable implements vscode.WebviewPanel { export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape, extHostProtocol.ExtHostWebviewPanelsShape { - private static newHandle(): extHostProtocol.WebviewPanelHandle { + private static newHandle(): extHostProtocol.WebviewHandle { return generateUuid(); } private readonly _proxy: extHostProtocol.MainThreadWebviewPanelsAndViewsShape; private readonly _webviewProxy: extHostProtocol.MainThreadWebviewsShape; - private readonly _webviews = new Map(); - private readonly _webviewPanels = new Map(); + private readonly _webviews = new Map(); + private readonly _webviewPanels = new Map(); private readonly _serializers = new Map { + async $onDidDisposeWebviewPanel(handle: extHostProtocol.WebviewHandle): Promise { const panel = this.getWebviewPanel(handle); panel?.dispose(); @@ -391,7 +391,7 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape, ex } async $deserializeWebviewPanel( - webviewHandle: extHostProtocol.WebviewPanelHandle, + webviewHandle: extHostProtocol.WebviewHandle, viewType: string, title: string, state: any, @@ -424,11 +424,11 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape, ex return webview; } - private getWebview(handle: extHostProtocol.WebviewPanelHandle): ExtHostWebview | undefined { + private getWebview(handle: extHostProtocol.WebviewHandle): ExtHostWebview | undefined { return this._webviews.get(handle); } - public getWebviewPanel(handle: extHostProtocol.WebviewPanelHandle): ExtHostWebviewPanel | undefined { + public getWebviewPanel(handle: extHostProtocol.WebviewHandle): ExtHostWebviewPanel | undefined { return this._webviewPanels.get(handle); } } diff --git a/src/vs/workbench/api/common/extHostWebviewView.ts b/src/vs/workbench/api/common/extHostWebviewView.ts index 86da3876486..cc23d2a1fcd 100644 --- a/src/vs/workbench/api/common/extHostWebviewView.ts +++ b/src/vs/workbench/api/common/extHostWebviewView.ts @@ -14,7 +14,7 @@ import * as extHostTypes from './extHostTypes'; class ExtHostWebviewView extends Disposable implements vscode.WebviewView { - readonly #handle: extHostProtocol.WebviewPanelHandle; + readonly #handle: extHostProtocol.WebviewHandle; readonly #proxy: extHostProtocol.MainThreadWebviewViewsShape; readonly #viewType: string; @@ -25,7 +25,7 @@ class ExtHostWebviewView extends Disposable implements vscode.WebviewView { #title: string | undefined; constructor( - handle: extHostProtocol.WebviewPanelHandle, + handle: extHostProtocol.WebviewHandle, proxy: extHostProtocol.MainThreadWebviewViewsShape, viewType: string, webview: ExtHostWebview, @@ -101,7 +101,7 @@ export class ExtHostWebviewViews implements extHostProtocol.ExtHostWebviewViewsS readonly extension: IExtensionDescription; }>(); - private readonly _webviewViews = new Map(); + private readonly _webviewViews = new Map(); constructor( mainContext: extHostProtocol.IMainContext, From 72cbf699d82fc30b4e426e9f5a2b88ee5508552e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 26 Aug 2020 16:21:19 -0700 Subject: [PATCH 578/736] Split out main thread webview panels into own class/file --- .../api/browser/extensionHost.contribution.ts | 2 +- .../api/browser/mainThreadCustomEditors.ts | 8 ++--- .../api/browser/mainThreadWebviewManager.ts | 35 +++++++++++++++++++ ...AndViews.ts => mainThreadWebviewPanels.ts} | 20 ++--------- .../workbench/api/common/extHost.protocol.ts | 4 +-- src/vs/workbench/api/common/extHostWebview.ts | 8 ++--- .../test/browser/api/extHostWebview.test.ts | 4 +-- 7 files changed, 50 insertions(+), 31 deletions(-) create mode 100644 src/vs/workbench/api/browser/mainThreadWebviewManager.ts rename src/vs/workbench/api/browser/{mainThreadWebviewPanelsAndViews.ts => mainThreadWebviewPanels.ts} (90%) diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index 5a9674d46bc..bfabf000891 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -54,7 +54,7 @@ import './mainThreadTreeViews'; import './mainThreadDownloadService'; import './mainThreadUrls'; import './mainThreadWindow'; -import './mainThreadWebviewPanelsAndViews'; +import './mainThreadWebviewManager'; import './mainThreadWorkspace'; import './mainThreadComments'; import './mainThreadNotebook'; diff --git a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts index b3d73594e6f..9bd40eeb40d 100644 --- a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts @@ -19,7 +19,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo'; -import type { MainThreadWebviewPanelsAndViews } from 'vs/workbench/api/browser/mainThreadWebviewPanelsAndViews'; +import { MainThreadWebviewPanels } from 'vs/workbench/api/browser/mainThreadWebviewPanels'; import { MainThreadWebviews, reviveWebviewExtension } from 'vs/workbench/api/browser/mainThreadWebviews'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { editorGroupToViewColumn } from 'vs/workbench/api/common/shared/editor'; @@ -49,7 +49,7 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc constructor( private readonly mainThreadWebview: MainThreadWebviews, - private readonly mainThreadWebviewPanelsAndViews: MainThreadWebviewPanelsAndViews, + private readonly mainThreadWebviewPanels: MainThreadWebviewPanels, context: extHostProtocol.IExtHostContext, @IWorkingCopyService workingCopyService: IWorkingCopyService, @IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService, @@ -121,7 +121,7 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc const handle = webviewInput.id; const resource = webviewInput.resource; - this.mainThreadWebviewPanelsAndViews.addWebviewInput(handle, webviewInput); + this.mainThreadWebviewPanels.addWebviewInput(handle, webviewInput); webviewInput.webview.options = options; webviewInput.webview.extension = extension; @@ -210,7 +210,7 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc case CustomEditorModelType.Custom: { const model = MainThreadCustomEditorModel.create(this._instantiationService, this._proxyCustomEditors, viewType, resource, options, () => { - return Array.from(this.mainThreadWebviewPanelsAndViews.webviewInputs) + return Array.from(this.mainThreadWebviewPanels.webviewInputs) .filter(editor => editor instanceof CustomEditorInput && isEqual(editor.resource, resource)) as CustomEditorInput[]; }, cancellation, this._backupService); return this._customEditorService.models.add(resource, viewType, model); diff --git a/src/vs/workbench/api/browser/mainThreadWebviewManager.ts b/src/vs/workbench/api/browser/mainThreadWebviewManager.ts new file mode 100644 index 00000000000..abe953dd010 --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadWebviewManager.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from 'vs/base/common/lifecycle'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { MainThreadCustomEditors } from 'vs/workbench/api/browser/mainThreadCustomEditors'; +import { MainThreadWebviewPanels } from 'vs/workbench/api/browser/mainThreadWebviewPanels'; +import { MainThreadWebviews } from 'vs/workbench/api/browser/mainThreadWebviews'; +import { MainThreadWebviewsViews } from 'vs/workbench/api/browser/mainThreadWebviewViews'; +import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; +import { extHostCustomer } from '../common/extHostCustomers'; + +@extHostCustomer +export class MainThreadWebviewManager extends Disposable { + constructor( + context: extHostProtocol.IExtHostContext, + @IInstantiationService instantiationService: IInstantiationService, + ) { + super(); + + const webviews = this._register(instantiationService.createInstance(MainThreadWebviews, context)); + context.set(extHostProtocol.MainContext.MainThreadWebviews, webviews); + + const webviewPanels = this._register(instantiationService.createInstance(MainThreadWebviewPanels, webviews, context)); + context.set(extHostProtocol.MainContext.MainThreadWebviewPanels, webviewPanels); + + const customEditors = this._register(instantiationService.createInstance(MainThreadCustomEditors, webviews, webviewPanels, context)); + context.set(extHostProtocol.MainContext.MainThreadCustomEditors, customEditors); + + const webviewViews = this._register(instantiationService.createInstance(MainThreadWebviewsViews, webviews, context)); + context.set(extHostProtocol.MainContext.MainThreadWebviewViews, webviewViews); + } +} diff --git a/src/vs/workbench/api/browser/mainThreadWebviewPanelsAndViews.ts b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts similarity index 90% rename from src/vs/workbench/api/browser/mainThreadWebviewPanelsAndViews.ts rename to src/vs/workbench/api/browser/mainThreadWebviewPanels.ts index ea94413c303..b089b04b6dd 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewPanelsAndViews.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts @@ -6,11 +6,8 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { MainThreadCustomEditors } from 'vs/workbench/api/browser/mainThreadCustomEditors'; import { MainThreadWebviews, reviveWebviewExtension, reviveWebviewOptions } from 'vs/workbench/api/browser/mainThreadWebviews'; -import { MainThreadWebviewsViews } from 'vs/workbench/api/browser/mainThreadWebviewViews'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; import { IEditorInput } from 'vs/workbench/common/editor'; @@ -22,7 +19,6 @@ import { ICreateWebViewShowOptions, IWebviewWorkbenchService, WebviewInputOption import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { extHostNamedCustomer } from '../common/extHostCustomers'; /** * Bi-directional map between webview handles and inputs. @@ -77,8 +73,7 @@ class WebviewViewTypeTransformer { } } -@extHostNamedCustomer(extHostProtocol.MainContext.MainThreadWebviewService) -export class MainThreadWebviewPanelsAndViews extends Disposable implements extHostProtocol.MainThreadWebviewPanelsAndViewsShape { +export class MainThreadWebviewPanels extends Disposable implements extHostProtocol.MainThreadWebviewPanelsShape { private readonly webviewPanelViewType = new WebviewViewTypeTransformer('mainThreadWebview-'); @@ -89,32 +84,21 @@ export class MainThreadWebviewPanelsAndViews extends Disposable implements extHo private readonly _editorProviders = new Map(); private readonly _webviewFromDiffEditorHandles = new Set(); - private readonly _mainThreadWebviews: MainThreadWebviews; - private readonly _revivers = new Map(); constructor( + private readonly _mainThreadWebviews: MainThreadWebviews, context: extHostProtocol.IExtHostContext, @IExtensionService extensionService: IExtensionService, @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, @IEditorService private readonly _editorService: IEditorService, @ITelemetryService private readonly _telemetryService: ITelemetryService, @IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, ) { super(); this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewPanels); - this._mainThreadWebviews = this._instantiationService.createInstance(MainThreadWebviews, context); - context.set(extHostProtocol.MainContext.MainThreadWebviews, this._mainThreadWebviews); - - const webviewViews = this._instantiationService.createInstance(MainThreadWebviewsViews, this._mainThreadWebviews, context); - context.set(extHostProtocol.MainContext.MainThreadWebviewViews, webviewViews); - - const customEditors = this._instantiationService.createInstance(MainThreadCustomEditors, this._mainThreadWebviews, this, context); - context.set(extHostProtocol.MainContext.MainThreadCustomEditors, customEditors); - this._register(_editorService.onDidActiveEditorChange(() => { const activeInput = this._editorService.activeEditor; if (activeInput instanceof DiffEditorInput && activeInput.primary instanceof WebviewInput && activeInput.secondary instanceof WebviewInput) { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 10ad9f81831..5156730c7f6 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -614,7 +614,7 @@ export interface MainThreadWebviewsShape extends IDisposable { $postMessage(handle: WebviewHandle, value: any): Promise } -export interface MainThreadWebviewPanelsAndViewsShape extends IDisposable { +export interface MainThreadWebviewPanelsShape extends IDisposable { $createWebviewPanel(extension: WebviewExtensionDescription, handle: WebviewHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: modes.IWebviewPanelOptions & modes.IWebviewOptions): void; $disposeWebview(handle: WebviewHandle): void; $reveal(handle: WebviewHandle, showOptions: WebviewPanelShowOptions): void; @@ -1720,8 +1720,8 @@ export const MainContext = { MainThreadStorage: createMainId('MainThreadStorage'), MainThreadTelemetry: createMainId('MainThreadTelemetry'), MainThreadTerminalService: createMainId('MainThreadTerminalService'), - MainThreadWebviewService: createMainId('MainThreadWebviewService'), MainThreadWebviews: createMainId('MainThreadWebviews'), + MainThreadWebviewPanels: createMainId('MainThreadWebviewPanels'), MainThreadWebviewViews: createMainId('MainThreadWebviewViews'), MainThreadCustomEditors: createMainId('MainThreadCustomEditors'), MainThreadUrls: createMainId('MainThreadUrls'), diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index a7836032cd9..3d2673c7142 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -122,7 +122,7 @@ type IconPath = URI | { light: URI, dark: URI }; class ExtHostWebviewPanel extends Disposable implements vscode.WebviewPanel { readonly #handle: extHostProtocol.WebviewHandle; - readonly #proxy: extHostProtocol.MainThreadWebviewPanelsAndViewsShape; + readonly #proxy: extHostProtocol.MainThreadWebviewPanelsShape; readonly #viewType: string; readonly #webview: ExtHostWebview; @@ -143,7 +143,7 @@ class ExtHostWebviewPanel extends Disposable implements vscode.WebviewPanel { constructor( handle: extHostProtocol.WebviewHandle, - proxy: extHostProtocol.MainThreadWebviewPanelsAndViewsShape, + proxy: extHostProtocol.MainThreadWebviewPanelsShape, viewType: string, title: string, viewColumn: vscode.ViewColumn | undefined, @@ -269,7 +269,7 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape, ex return generateUuid(); } - private readonly _proxy: extHostProtocol.MainThreadWebviewPanelsAndViewsShape; + private readonly _proxy: extHostProtocol.MainThreadWebviewPanelsShape; private readonly _webviewProxy: extHostProtocol.MainThreadWebviewsShape; private readonly _webviews = new Map(); @@ -287,7 +287,7 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape, ex private readonly _logService: ILogService, private readonly _deprecationService: IExtHostApiDeprecationService, ) { - this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadWebviewService); + this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadWebviewPanels); this._webviewProxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadWebviews); } diff --git a/src/vs/workbench/test/browser/api/extHostWebview.test.ts b/src/vs/workbench/test/browser/api/extHostWebview.test.ts index 31e8bf7bacf..9e6194a99c7 100644 --- a/src/vs/workbench/test/browser/api/extHostWebview.test.ts +++ b/src/vs/workbench/test/browser/api/extHostWebview.test.ts @@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { mock } from 'vs/base/test/common/mock'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { NullLogService } from 'vs/platform/log/common/log'; -import { MainThreadWebviewPanelsAndViews } from 'vs/workbench/api/browser/mainThreadWebviewPanelsAndViews'; +import { MainThreadWebviewManager } from 'vs/workbench/api/browser/mainThreadWebviewManager'; import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { NullApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; @@ -150,7 +150,7 @@ suite('ExtHostWebview', () => { function createNoopMainThreadWebviews() { - return new class extends mock() { + return new class extends mock() { $createWebviewPanel() { /* noop */ } $registerSerializer() { /* noop */ } $unregisterSerializer() { /* noop */ } From 23fe7a39de1cf6eff8044b8ea1ff30d692b98f6f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 26 Aug 2020 16:44:53 -0700 Subject: [PATCH 579/736] Split extHost webview panels to own file --- .../workbench/api/common/extHost.api.impl.ts | 9 +- .../api/common/extHostCustomEditors.ts | 6 +- src/vs/workbench/api/common/extHostWebview.ts | 283 +---------------- .../api/common/extHostWebviewPanels.ts | 299 ++++++++++++++++++ .../test/browser/api/extHostWebview.test.ts | 23 +- 5 files changed, 330 insertions(+), 290 deletions(-) create mode 100644 src/vs/workbench/api/common/extHostWebviewPanels.ts diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 0785eb62371..864c337d345 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -78,6 +78,7 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer'; import { ExtHostWebviewViews } from 'vs/workbench/api/common/extHostWebviewView'; import { ExtHostCustomEditors } from 'vs/workbench/api/common/extHostCustomEditors'; +import { ExtHostWebviewPanels } from 'vs/workbench/api/common/extHostWebviewPanels'; export interface IExtensionApiFactory { (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; @@ -144,8 +145,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol)); const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol, extHostCommands)); const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace, extHostLogService, extHostApiDeprecation)); - rpcProtocol.set(ExtHostContext.ExtHostWebviewPanels, extHostWebviews); - const extHostCustomEditors = rpcProtocol.set(ExtHostContext.ExtHostCustomEditors, new ExtHostCustomEditors(rpcProtocol, extHostDocuments, extensionStoragePaths, extHostWebviews)); + const extHostWebviewPanels = rpcProtocol.set(ExtHostContext.ExtHostWebviewPanels, new ExtHostWebviewPanels(rpcProtocol, extHostWebviews, extHostWorkspace)); + const extHostCustomEditors = rpcProtocol.set(ExtHostContext.ExtHostCustomEditors, new ExtHostCustomEditors(rpcProtocol, extHostDocuments, extensionStoragePaths, extHostWebviews, extHostWebviewPanels)); const extHostWebviewViews = rpcProtocol.set(ExtHostContext.ExtHostWebviewViews, new ExtHostWebviewViews(rpcProtocol, extHostWebviews)); // Check that no named customers are missing @@ -569,7 +570,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostOutputService.createOutputChannel(name); }, createWebviewPanel(viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, options?: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel { - return extHostWebviews.createWebviewPanel(extension, viewType, title, showOptions, options); + return extHostWebviewPanels.createWebviewPanel(extension, viewType, title, showOptions, options); }, createWebviewTextEditorInset(editor: vscode.TextEditor, line: number, height: number, options?: vscode.WebviewOptions): vscode.WebviewEditorInset { checkProposedApiEnabled(extension); @@ -594,7 +595,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostTreeViews.createTreeView(viewId, options, extension); }, registerWebviewPanelSerializer: (viewType: string, serializer: vscode.WebviewPanelSerializer) => { - return extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializer); + return extHostWebviewPanels.registerWebviewPanelSerializer(extension, viewType, serializer); }, registerCustomEditorProvider: (viewType: string, provider: vscode.CustomTextEditorProvider | vscode.CustomReadonlyEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerDocument?: boolean } = {}) => { return extHostCustomEditors.registerCustomEditorProvider(extension, viewType, provider, options); diff --git a/src/vs/workbench/api/common/extHostCustomEditors.ts b/src/vs/workbench/api/common/extHostCustomEditors.ts index 779075db693..f3b3cbd8438 100644 --- a/src/vs/workbench/api/common/extHostCustomEditors.ts +++ b/src/vs/workbench/api/common/extHostCustomEditors.ts @@ -14,6 +14,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; import { ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview'; +import { ExtHostWebviewPanels } from 'vs/workbench/api/common/extHostWebviewPanels'; import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; import type * as vscode from 'vscode'; import { Cache } from './cache'; @@ -165,6 +166,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor private readonly _extHostDocuments: ExtHostDocuments, private readonly _extensionStoragePaths: IExtensionStoragePaths | undefined, private readonly _extHostWebview: ExtHostWebviews, + private readonly _extHostWebviewPanels: ExtHostWebviewPanels, ) { this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadCustomEditors); } @@ -260,7 +262,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor } const webview = this._extHostWebview.createNewWebview(handle, options, entry.extension); - const panel = this._extHostWebview.createNewWebviewPanel(handle, viewType, title, position, options, webview); + const panel = this._extHostWebviewPanels.createNewWebviewPanel(handle, viewType, title, position, options, webview); const revivedResource = URI.revive(resource); @@ -297,7 +299,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor throw new Error(`Provider does not implement move '${viewType}'`); } - const webview = this._extHostWebview.getWebviewPanel(handle); + const webview = this._extHostWebviewPanels.getWebviewPanel(handle); if (!webview) { throw new Error(`No webview found`); } diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index 3d2673c7142..e5dc3c670d5 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -4,20 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { generateUuid } from 'vs/base/common/uuid'; import * as modes from 'vs/editor/common/modes'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; -import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; -import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; import type * as vscode from 'vscode'; import * as extHostProtocol from './extHost.protocol'; -import * as extHostTypes from './extHostTypes'; export class ExtHostWebview implements vscode.Webview { @@ -116,169 +111,11 @@ export class ExtHostWebview implements vscode.Webview { } } -type IconPath = URI | { light: URI, dark: URI }; +export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { - -class ExtHostWebviewPanel extends Disposable implements vscode.WebviewPanel { - - readonly #handle: extHostProtocol.WebviewHandle; - readonly #proxy: extHostProtocol.MainThreadWebviewPanelsShape; - readonly #viewType: string; - - readonly #webview: ExtHostWebview; - readonly #options: vscode.WebviewPanelOptions; - - #title: string; - #iconPath?: IconPath; - #viewColumn: vscode.ViewColumn | undefined = undefined; - #visible: boolean = true; - #active: boolean = true; - #isDisposed: boolean = false; - - readonly #onDidDispose = this._register(new Emitter()); - public readonly onDidDispose = this.#onDidDispose.event; - - readonly #onDidChangeViewState = this._register(new Emitter()); - public readonly onDidChangeViewState = this.#onDidChangeViewState.event; - - constructor( - handle: extHostProtocol.WebviewHandle, - proxy: extHostProtocol.MainThreadWebviewPanelsShape, - viewType: string, - title: string, - viewColumn: vscode.ViewColumn | undefined, - editorOptions: vscode.WebviewPanelOptions, - webview: ExtHostWebview - ) { - super(); - this.#handle = handle; - this.#proxy = proxy; - this.#viewType = viewType; - this.#options = editorOptions; - this.#viewColumn = viewColumn; - this.#title = title; - this.#webview = webview; - } - - public dispose() { - if (this.#isDisposed) { - return; - } - - this.#isDisposed = true; - this.#onDidDispose.fire(); - - this.#proxy.$disposeWebview(this.#handle); - this.#webview.dispose(); - - super.dispose(); - } - - get webview() { - this.assertNotDisposed(); - return this.#webview; - } - - get viewType(): string { - this.assertNotDisposed(); - return this.#viewType; - } - - get title(): string { - this.assertNotDisposed(); - return this.#title; - } - - set title(value: string) { - this.assertNotDisposed(); - if (this.#title !== value) { - this.#title = value; - this.#proxy.$setTitle(this.#handle, value); - } - } - - get iconPath(): IconPath | undefined { - this.assertNotDisposed(); - return this.#iconPath; - } - - set iconPath(value: IconPath | undefined) { - this.assertNotDisposed(); - if (this.#iconPath !== value) { - this.#iconPath = value; - - this.#proxy.$setIconPath(this.#handle, URI.isUri(value) ? { light: value, dark: value } : value); - } - } - - get options() { - return this.#options; - } - - get viewColumn(): vscode.ViewColumn | undefined { - this.assertNotDisposed(); - if (typeof this.#viewColumn === 'number' && 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; - } - - public get active(): boolean { - this.assertNotDisposed(); - return this.#active; - } - - public get visible(): boolean { - this.assertNotDisposed(); - return this.#visible; - } - - _updateViewState(newState: { active: boolean; visible: boolean; viewColumn: vscode.ViewColumn; }) { - if (this.#isDisposed) { - return; - } - - if (this.active !== newState.active || this.visible !== newState.visible || this.viewColumn !== newState.viewColumn) { - this.#active = newState.active; - this.#visible = newState.visible; - this.#viewColumn = newState.viewColumn; - this.#onDidChangeViewState.fire({ webviewPanel: this }); - } - } - - public reveal(viewColumn?: vscode.ViewColumn, preserveFocus?: boolean): void { - this.assertNotDisposed(); - this.#proxy.$reveal(this.#handle, { - viewColumn: viewColumn ? typeConverters.ViewColumn.from(viewColumn) : undefined, - preserveFocus: !!preserveFocus - }); - } - - private assertNotDisposed() { - if (this.#isDisposed) { - throw new Error('Webview is disposed'); - } - } -} - -export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape, extHostProtocol.ExtHostWebviewPanelsShape { - - private static newHandle(): extHostProtocol.WebviewHandle { - return generateUuid(); - } - - private readonly _proxy: extHostProtocol.MainThreadWebviewPanelsShape; private readonly _webviewProxy: extHostProtocol.MainThreadWebviewsShape; private readonly _webviews = new Map(); - private readonly _webviewPanels = new Map(); - - private readonly _serializers = new Map(); constructor( mainContext: extHostProtocol.IMainContext, @@ -287,32 +124,9 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape, ex private readonly _logService: ILogService, private readonly _deprecationService: IExtHostApiDeprecationService, ) { - this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadWebviewPanels); this._webviewProxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadWebviews); } - public createWebviewPanel( - extension: IExtensionDescription, - viewType: string, - title: string, - showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, - options: (vscode.WebviewPanelOptions & vscode.WebviewOptions) = {}, - ): vscode.WebviewPanel { - const viewColumn = typeof showOptions === 'object' ? showOptions.viewColumn : showOptions; - const webviewShowOptions = { - viewColumn: typeConverters.ViewColumn.from(viewColumn), - preserveFocus: typeof showOptions === 'object' && !!showOptions.preserveFocus - }; - - const handle = ExtHostWebviews.newHandle(); - this._proxy.$createWebviewPanel(toExtensionData(extension), handle, viewType, title, webviewShowOptions, convertWebviewOptions(extension, this.workspace, options)); - - const webview = this.createNewWebview(handle, options, extension); - const panel = this.createNewWebviewPanel(handle, viewType, title, viewColumn, options, webview); - - return panel; - } - public $onMessage( handle: extHostProtocol.WebviewHandle, message: any @@ -330,91 +144,6 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape, ex this._logService.warn(`${extensionId} created a webview without a content security policy: https://aka.ms/vscode-webview-missing-csp`); } - public $onDidChangeWebviewPanelViewStates(newStates: extHostProtocol.WebviewPanelViewStateData): void { - const handles = Object.keys(newStates); - // Notify webviews of state changes in the following order: - // - Non-visible - // - Visible - // - Active - handles.sort((a, b) => { - const stateA = newStates[a]; - const stateB = newStates[b]; - if (stateA.active) { - return 1; - } - if (stateB.active) { - return -1; - } - return (+stateA.visible) - (+stateB.visible); - }); - - for (const handle of handles) { - const panel = this.getWebviewPanel(handle); - if (!panel) { - continue; - } - - const newState = newStates[handle]; - panel._updateViewState({ - active: newState.active, - visible: newState.visible, - viewColumn: typeConverters.ViewColumn.to(newState.position), - }); - } - } - - async $onDidDisposeWebviewPanel(handle: extHostProtocol.WebviewHandle): Promise { - const panel = this.getWebviewPanel(handle); - panel?.dispose(); - - this._webviewPanels.delete(handle); - this._webviews.delete(handle); - } - - - public registerWebviewPanelSerializer( - extension: IExtensionDescription, - viewType: string, - serializer: vscode.WebviewPanelSerializer - ): vscode.Disposable { - if (this._serializers.has(viewType)) { - throw new Error(`Serializer for '${viewType}' already registered`); - } - - this._serializers.set(viewType, { serializer, extension }); - this._proxy.$registerSerializer(viewType); - - return new extHostTypes.Disposable(() => { - this._serializers.delete(viewType); - this._proxy.$unregisterSerializer(viewType); - }); - } - - async $deserializeWebviewPanel( - webviewHandle: extHostProtocol.WebviewHandle, - viewType: string, - title: string, - state: any, - position: EditorViewColumn, - options: modes.IWebviewOptions & modes.IWebviewPanelOptions - ): Promise { - const entry = this._serializers.get(viewType); - if (!entry) { - throw new Error(`No serializer found for '${viewType}'`); - } - const { serializer, extension } = entry; - - const webview = this.createNewWebview(webviewHandle, options, extension); - const revivedPanel = this.createNewWebviewPanel(webviewHandle, viewType, title, position, options, webview); - await serializer.deserializeWebviewPanel(revivedPanel, state); - } - - public createNewWebviewPanel(webviewHandle: string, viewType: string, title: string, position: number, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, webview: ExtHostWebview) { - const panel = new ExtHostWebviewPanel(webviewHandle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview); - this._webviewPanels.set(webviewHandle, panel); - return panel; - } - public createNewWebview(handle: string, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, extension: IExtensionDescription): ExtHostWebview { const webview = new ExtHostWebview(handle, this._webviewProxy, reviveOptions(options), this.initData, this.workspace, extension, this._deprecationService); this._webviews.set(handle, webview); @@ -424,12 +153,12 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape, ex return webview; } - private getWebview(handle: extHostProtocol.WebviewHandle): ExtHostWebview | undefined { - return this._webviews.get(handle); + public deleteWebview(handle: string) { + this._webviews.delete(handle); } - public getWebviewPanel(handle: extHostProtocol.WebviewHandle): ExtHostWebviewPanel | undefined { - return this._webviewPanels.get(handle); + private getWebview(handle: extHostProtocol.WebviewHandle): ExtHostWebview | undefined { + return this._webviews.get(handle); } } @@ -437,7 +166,7 @@ export function toExtensionData(extension: IExtensionDescription): extHostProtoc return { id: extension.identifier, location: extension.extensionLocation }; } -function convertWebviewOptions( +export function convertWebviewOptions( extension: IExtensionDescription, workspace: IExtHostWorkspace | undefined, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, diff --git a/src/vs/workbench/api/common/extHostWebviewPanels.ts b/src/vs/workbench/api/common/extHostWebviewPanels.ts new file mode 100644 index 00000000000..d29fdc631fa --- /dev/null +++ b/src/vs/workbench/api/common/extHostWebviewPanels.ts @@ -0,0 +1,299 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { generateUuid } from 'vs/base/common/uuid'; +import * as modes from 'vs/editor/common/modes'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; +import { convertWebviewOptions, ExtHostWebview, ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview'; +import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; +import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; +import type * as vscode from 'vscode'; +import * as extHostProtocol from './extHost.protocol'; +import * as extHostTypes from './extHostTypes'; + + +type IconPath = URI | { light: URI, dark: URI }; + +class ExtHostWebviewPanel extends Disposable implements vscode.WebviewPanel { + + readonly #handle: extHostProtocol.WebviewHandle; + readonly #proxy: extHostProtocol.MainThreadWebviewPanelsShape; + readonly #viewType: string; + + readonly #webview: ExtHostWebview; + readonly #options: vscode.WebviewPanelOptions; + + #title: string; + #iconPath?: IconPath; + #viewColumn: vscode.ViewColumn | undefined = undefined; + #visible: boolean = true; + #active: boolean = true; + #isDisposed: boolean = false; + + readonly #onDidDispose = this._register(new Emitter()); + public readonly onDidDispose = this.#onDidDispose.event; + + readonly #onDidChangeViewState = this._register(new Emitter()); + public readonly onDidChangeViewState = this.#onDidChangeViewState.event; + + constructor( + handle: extHostProtocol.WebviewHandle, + proxy: extHostProtocol.MainThreadWebviewPanelsShape, + viewType: string, + title: string, + viewColumn: vscode.ViewColumn | undefined, + editorOptions: vscode.WebviewPanelOptions, + webview: ExtHostWebview + ) { + super(); + this.#handle = handle; + this.#proxy = proxy; + this.#viewType = viewType; + this.#options = editorOptions; + this.#viewColumn = viewColumn; + this.#title = title; + this.#webview = webview; + } + + public dispose() { + if (this.#isDisposed) { + return; + } + + this.#isDisposed = true; + this.#onDidDispose.fire(); + + this.#proxy.$disposeWebview(this.#handle); + this.#webview.dispose(); + + super.dispose(); + } + + get webview() { + this.assertNotDisposed(); + return this.#webview; + } + + get viewType(): string { + this.assertNotDisposed(); + return this.#viewType; + } + + get title(): string { + this.assertNotDisposed(); + return this.#title; + } + + set title(value: string) { + this.assertNotDisposed(); + if (this.#title !== value) { + this.#title = value; + this.#proxy.$setTitle(this.#handle, value); + } + } + + get iconPath(): IconPath | undefined { + this.assertNotDisposed(); + return this.#iconPath; + } + + set iconPath(value: IconPath | undefined) { + this.assertNotDisposed(); + if (this.#iconPath !== value) { + this.#iconPath = value; + + this.#proxy.$setIconPath(this.#handle, URI.isUri(value) ? { light: value, dark: value } : value); + } + } + + get options() { + return this.#options; + } + + get viewColumn(): vscode.ViewColumn | undefined { + this.assertNotDisposed(); + if (typeof this.#viewColumn === 'number' && 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; + } + + public get active(): boolean { + this.assertNotDisposed(); + return this.#active; + } + + public get visible(): boolean { + this.assertNotDisposed(); + return this.#visible; + } + + _updateViewState(newState: { active: boolean; visible: boolean; viewColumn: vscode.ViewColumn; }) { + if (this.#isDisposed) { + return; + } + + if (this.active !== newState.active || this.visible !== newState.visible || this.viewColumn !== newState.viewColumn) { + this.#active = newState.active; + this.#visible = newState.visible; + this.#viewColumn = newState.viewColumn; + this.#onDidChangeViewState.fire({ webviewPanel: this }); + } + } + + public reveal(viewColumn?: vscode.ViewColumn, preserveFocus?: boolean): void { + this.assertNotDisposed(); + this.#proxy.$reveal(this.#handle, { + viewColumn: viewColumn ? typeConverters.ViewColumn.from(viewColumn) : undefined, + preserveFocus: !!preserveFocus + }); + } + + private assertNotDisposed() { + if (this.#isDisposed) { + throw new Error('Webview is disposed'); + } + } +} + +export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanelsShape { + + private static newHandle(): extHostProtocol.WebviewHandle { + return generateUuid(); + } + + private readonly _proxy: extHostProtocol.MainThreadWebviewPanelsShape; + + private readonly _webviewPanels = new Map(); + + private readonly _serializers = new Map(); + + constructor( + mainContext: extHostProtocol.IMainContext, + private readonly webviews: ExtHostWebviews, + private readonly workspace: IExtHostWorkspace | undefined, + ) { + this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadWebviewPanels); + } + + public createWebviewPanel( + extension: IExtensionDescription, + viewType: string, + title: string, + showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, + options: (vscode.WebviewPanelOptions & vscode.WebviewOptions) = {}, + ): vscode.WebviewPanel { + const viewColumn = typeof showOptions === 'object' ? showOptions.viewColumn : showOptions; + const webviewShowOptions = { + viewColumn: typeConverters.ViewColumn.from(viewColumn), + preserveFocus: typeof showOptions === 'object' && !!showOptions.preserveFocus + }; + + const handle = ExtHostWebviewPanels.newHandle(); + this._proxy.$createWebviewPanel(toExtensionData(extension), handle, viewType, title, webviewShowOptions, convertWebviewOptions(extension, this.workspace, options)); + + const webview = this.webviews.createNewWebview(handle, options, extension); + const panel = this.createNewWebviewPanel(handle, viewType, title, viewColumn, options, webview); + + return panel; + } + + public $onDidChangeWebviewPanelViewStates(newStates: extHostProtocol.WebviewPanelViewStateData): void { + const handles = Object.keys(newStates); + // Notify webviews of state changes in the following order: + // - Non-visible + // - Visible + // - Active + handles.sort((a, b) => { + const stateA = newStates[a]; + const stateB = newStates[b]; + if (stateA.active) { + return 1; + } + if (stateB.active) { + return -1; + } + return (+stateA.visible) - (+stateB.visible); + }); + + for (const handle of handles) { + const panel = this.getWebviewPanel(handle); + if (!panel) { + continue; + } + + const newState = newStates[handle]; + panel._updateViewState({ + active: newState.active, + visible: newState.visible, + viewColumn: typeConverters.ViewColumn.to(newState.position), + }); + } + } + + async $onDidDisposeWebviewPanel(handle: extHostProtocol.WebviewHandle): Promise { + const panel = this.getWebviewPanel(handle); + panel?.dispose(); + + this._webviewPanels.delete(handle); + this.webviews.deleteWebview(handle); + } + + public registerWebviewPanelSerializer( + extension: IExtensionDescription, + viewType: string, + serializer: vscode.WebviewPanelSerializer + ): vscode.Disposable { + if (this._serializers.has(viewType)) { + throw new Error(`Serializer for '${viewType}' already registered`); + } + + this._serializers.set(viewType, { serializer, extension }); + this._proxy.$registerSerializer(viewType); + + return new extHostTypes.Disposable(() => { + this._serializers.delete(viewType); + this._proxy.$unregisterSerializer(viewType); + }); + } + + async $deserializeWebviewPanel( + webviewHandle: extHostProtocol.WebviewHandle, + viewType: string, + title: string, + state: any, + position: EditorViewColumn, + options: modes.IWebviewOptions & modes.IWebviewPanelOptions + ): Promise { + const entry = this._serializers.get(viewType); + if (!entry) { + throw new Error(`No serializer found for '${viewType}'`); + } + const { serializer, extension } = entry; + + const webview = this.webviews.createNewWebview(webviewHandle, options, extension); + const revivedPanel = this.createNewWebviewPanel(webviewHandle, viewType, title, position, options, webview); + await serializer.deserializeWebviewPanel(revivedPanel, state); + } + + public createNewWebviewPanel(webviewHandle: string, viewType: string, title: string, position: number, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, webview: ExtHostWebview) { + const panel = new ExtHostWebviewPanel(webviewHandle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview); + this._webviewPanels.set(webviewHandle, panel); + return panel; + } + + public getWebviewPanel(handle: extHostProtocol.WebviewHandle): ExtHostWebviewPanel | undefined { + return this._webviewPanels.get(handle); + } +} diff --git a/src/vs/workbench/test/browser/api/extHostWebview.test.ts b/src/vs/workbench/test/browser/api/extHostWebview.test.ts index 9e6194a99c7..577b61c1747 100644 --- a/src/vs/workbench/test/browser/api/extHostWebview.test.ts +++ b/src/vs/workbench/test/browser/api/extHostWebview.test.ts @@ -13,6 +13,7 @@ import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { NullApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview'; +import { ExtHostWebviewPanels } from 'vs/workbench/api/common/extHostWebviewPanels'; import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; import type * as vscode from 'vscode'; import { SingleProxyRPCProtocol } from './testRPCProtocol'; @@ -35,6 +36,8 @@ suite('ExtHostWebview', () => { isExtensionDevelopmentDebug: false, }, undefined, new NullLogService(), NullApiDeprecationService); + const extHostWebviewPanels = new ExtHostWebviewPanels(rpcProtocol!, extHostWebviews, undefined); + let lastInvokedDeserializer: vscode.WebviewPanelSerializer | undefined = undefined; class NoopSerializer implements vscode.WebviewPanelSerializer { @@ -48,20 +51,20 @@ suite('ExtHostWebview', () => { const serializerA = new NoopSerializer(); const serializerB = new NoopSerializer(); - const serializerARegistration = extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializerA); + const serializerARegistration = extHostWebviewPanels.registerWebviewPanelSerializer(extension, viewType, serializerA); - await extHostWebviews.$deserializeWebviewPanel('x', viewType, 'title', {}, 0 as EditorViewColumn, {}); + await extHostWebviewPanels.$deserializeWebviewPanel('x', viewType, 'title', {}, 0 as EditorViewColumn, {}); assert.strictEqual(lastInvokedDeserializer, serializerA); assert.throws( - () => extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializerB), + () => extHostWebviewPanels.registerWebviewPanelSerializer(extension, viewType, serializerB), 'Should throw when registering two serializers for the same view'); serializerARegistration.dispose(); - extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializerB); + extHostWebviewPanels.registerWebviewPanelSerializer(extension, viewType, serializerB); - await extHostWebviews.$deserializeWebviewPanel('x', viewType, 'title', {}, 0 as EditorViewColumn, {}); + await extHostWebviewPanels.$deserializeWebviewPanel('x', viewType, 'title', {}, 0 as EditorViewColumn, {}); assert.strictEqual(lastInvokedDeserializer, serializerB); }); @@ -71,7 +74,10 @@ suite('ExtHostWebview', () => { webviewResourceRoot: 'vscode-resource://{{resource}}', isExtensionDevelopmentDebug: false, }, undefined, new NullLogService(), NullApiDeprecationService); - const webview = extHostWebviews.createWebviewPanel({} as any, 'type', 'title', 1, {}); + + const extHostWebviewPanels = new ExtHostWebviewPanels(rpcProtocol!, extHostWebviews, undefined); + + const webview = extHostWebviewPanels.createWebviewPanel({} as any, 'type', 'title', 1, {}); assert.strictEqual( webview.webview.asWebviewUri(URI.parse('file:///Users/codey/file.html')).toString(), @@ -110,7 +116,10 @@ suite('ExtHostWebview', () => { webviewResourceRoot: `https://{{uuid}}.webview.contoso.com/commit/{{resource}}`, isExtensionDevelopmentDebug: false, }, undefined, new NullLogService(), NullApiDeprecationService); - const webview = extHostWebviews.createWebviewPanel({} as any, 'type', 'title', 1, {}); + + const extHostWebviewPanels = new ExtHostWebviewPanels(rpcProtocol!, extHostWebviews, undefined); + + const webview = extHostWebviewPanels.createWebviewPanel({} as any, 'type', 'title', 1, {}); function stripEndpointUuid(input: string) { return input.replace(/^https:\/\/[^\.]+?\./, ''); From 6b90f4d6dcfd26bed95d677408b6a68d0c45c3df Mon Sep 17 00:00:00 2001 From: Robo Date: Wed, 26 Aug 2020 16:55:03 -0700 Subject: [PATCH 580/736] chore: bump electron@9.2.1 (#105459) Fixes https://github.com/microsoft/vscode/issues/104636 --- .yarnrc | 2 +- cgmanifest.json | 4 ++-- package.json | 2 +- yarn.lock | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.yarnrc b/.yarnrc index 68cb12c1284..3c6eccfb102 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://atom.io/download/electron" -target "9.2.0" +target "9.2.1" runtime "electron" diff --git a/cgmanifest.json b/cgmanifest.json index 576724e75af..e6e8ce8a5c1 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -60,12 +60,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "0c2cb59b6283fe8d6bb4b14f8a832e2166aeaa0c" + "commitHash": "03c7a54dc534ce1867d4393b9b1a6989d4a7e005" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "9.2.0" + "version": "9.2.1" }, { "component": { diff --git a/package.json b/package.json index 057ff7ea4fe..f9a659186e8 100644 --- a/package.json +++ b/package.json @@ -109,7 +109,7 @@ "css-loader": "^3.2.0", "debounce": "^1.0.0", "deemon": "^1.4.0", - "electron": "9.2.0", + "electron": "9.2.1", "eslint": "6.8.0", "eslint-plugin-jsdoc": "^19.1.0", "event-stream": "3.3.4", diff --git a/yarn.lock b/yarn.lock index e813c24b15d..ef6d7c5760e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2744,10 +2744,10 @@ electron-to-chromium@^1.2.7: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.27.tgz#78ecb8a399066187bb374eede35d9c70565a803d" integrity sha1-eOy4o5kGYYe7N07t412ccFZagD0= -electron@9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/electron/-/electron-9.2.0.tgz#d9fc8c8c9e5109669c366bd7b9ba83b06095d7a4" - integrity sha512-4ecZ3rcGg//Gk4fAK3Jo61T+uh36JhU6HHR/PTujQqQiBw1g4tNPd4R2hGGth2d+7FkRIs5GdRNef7h64fQEMw== +electron@9.2.1: + version "9.2.1" + resolved "https://registry.yarnpkg.com/electron/-/electron-9.2.1.tgz#54ef574e1af4ae967b5efa94312f1b6458d44a02" + integrity sha512-ZsetaQjXB8+9/EFW1FnfK4ukpkwXCxMEaiKiUZhZ0ZLFlLnFCpe0Bg4vdDf7e4boWGcnlgN1jAJpBw7w0eXuqA== dependencies: "@electron/get" "^1.0.1" "@types/node" "^12.0.12" From b58f4549b2919fa3a5dc52df83f4ba60780c625f Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 26 Aug 2020 16:23:55 -0700 Subject: [PATCH 581/736] Implement notebook cell status bar item contributions Fix #104958 --- src/vs/vscode.proposed.d.ts | 40 ++++ .../api/browser/mainThreadNotebook.ts | 51 ++-- .../workbench/api/common/extHost.api.impl.ts | 7 +- .../workbench/api/common/extHost.protocol.ts | 5 +- .../workbench/api/common/extHostNotebook.ts | 213 ++++++++++++++++- src/vs/workbench/api/common/extHostTypes.ts | 6 + .../notebook/browser/media/notebook.css | 26 ++- .../notebook/browser/notebook.contribution.ts | 3 + .../notebook/browser/notebookBrowser.ts | 6 +- .../notebookCellStatusBarServiceImpl.ts | 54 +++++ .../notebook/browser/notebookEditorWidget.ts | 3 +- .../browser/view/renderers/cellRenderer.ts | 43 +--- .../browser/view/renderers/cellWidgets.ts | 217 ++++++++++++++++++ .../browser/view/renderers/codeCell.ts | 2 +- .../view/renderers/commonViewComponents.ts | 56 +---- .../browser/view/renderers/markdownCell.ts | 2 +- .../common/notebookCellStatusBarService.ts | 21 ++ .../contrib/notebook/common/notebookCommon.ts | 32 ++- 18 files changed, 662 insertions(+), 125 deletions(-) create mode 100644 src/vs/workbench/contrib/notebook/browser/notebookCellStatusBarServiceImpl.ts create mode 100644 src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts create mode 100644 src/vs/workbench/contrib/notebook/common/notebookCellStatusBarService.ts diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index ebaa7d6cf51..ba6a5453aac 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1613,6 +1613,35 @@ declare module 'vscode' { resolveKernel?(kernel: T, document: NotebookDocument, webview: NotebookCommunication, token: CancellationToken): ProviderResult; } + /** + * Represents the alignment of status bar items. + */ + export enum NotebookCellStatusBarAlignment { + + /** + * Aligned to the left side. + */ + Left = 1, + + /** + * Aligned to the right side. + */ + Right = 2 + } + + export interface NotebookCellStatusBarItem { + readonly cell: NotebookCell; + readonly alignment: NotebookCellStatusBarAlignment; + readonly priority?: number; + text: string; + tooltip: string | undefined; + command: string | Command | undefined; + accessibilityInformation?: AccessibilityInformation; + show(): void; + hide(): void; + dispose(): void; + } + export namespace notebook { export function registerNotebookContentProvider( notebookType: string, @@ -1670,6 +1699,17 @@ declare module 'vscode' { export function createConcatTextDocument(notebook: NotebookDocument, selector?: DocumentSelector): NotebookConcatTextDocument; export const onDidChangeActiveNotebookKernel: Event<{ document: NotebookDocument, kernel: NotebookKernel | undefined }>; + + /** + * Creates a notebook cell status bar [item](#NotebookCellStatusBarItem). + * It will be disposed automatically when the notebook document is closed or the cell is deleted. + * + * @param cell The cell on which this item should be shown. + * @param alignment The alignment of the item. + * @param priority The priority of the item. Higher values mean the item should be shown more to the left. + * @return A new status bar item. + */ + export function createCellStatusBarItem(cell: NotebookCell, alignment?: NotebookCellStatusBarAlignment, priority?: number): NotebookCellStatusBarItem; } //#endregion diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index a377146ad2b..cda01b3eab6 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -4,23 +4,23 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; -import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; -import { MainContext, MainThreadNotebookShape, NotebookExtensionDescription, IExtHostContext, ExtHostNotebookShape, ExtHostContext, INotebookDocumentsAndEditorsDelta } from '../common/extHost.protocol'; -import { Disposable, IDisposable, combinedDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { URI, UriComponents } from 'vs/base/common/uri'; -import { INotebookService, IMainNotebookController } from 'vs/workbench/contrib/notebook/common/notebookService'; -import { NOTEBOOK_DISPLAY_ORDER, NotebookCellOutputsSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, INotebookKernelInfo, INotebookKernelInfoDto, IEditor, INotebookDocumentFilter, DisplayOrderKey, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; -import { IRelativePattern } from 'vs/base/common/glob'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { Emitter } from 'vs/base/common/event'; +import { IRelativePattern } from 'vs/base/common/glob'; +import { combinedDisposable, Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; - +import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; +import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; +import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; +import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, DisplayOrderKey, ICellEditOperation, IEditor, INotebookDocumentFilter, INotebookKernelInfo, INotebookKernelInfoDto, NotebookCellMetadata, NotebookCellOutputsSplice, NotebookDocumentMetadata, NOTEBOOK_DISPLAY_ORDER, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, MainContext, MainThreadNotebookShape, NotebookExtensionDescription } from '../common/extHost.protocol'; class DocumentAndEditorState { static ofSets(before: Set, after: Set): { removed: T[], added: T[] } { @@ -137,6 +137,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo private _toDisposeOnEditorRemove = new Map(); private _currentState?: DocumentAndEditorState; private _editorEventListenersMapping: Map = new Map(); + private readonly _cellStatusBarEntries: Map = new Map(); constructor( extHostContext: IExtHostContext, @@ -144,8 +145,8 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo @IConfigurationService private readonly configurationService: IConfigurationService, @IEditorService private readonly editorService: IEditorService, @IAccessibilityService private readonly accessibilityService: IAccessibilityService, - @ILogService private readonly logService: ILogService - + @ILogService private readonly logService: ILogService, + @INotebookCellStatusBarService private readonly cellStatusBarService: INotebookCellStatusBarService ) { super(); this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebook); @@ -603,6 +604,24 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); textModel?.handleUnknownChange(); } + + async $setStatusBarEntry(id: number, rawStatusBarEntry: INotebookCellStatusBarEntryDto): Promise { + const statusBarEntry = { + ...rawStatusBarEntry, + ...{ cellResource: URI.revive(rawStatusBarEntry.cellResource) } + }; + + const existingEntry = this._cellStatusBarEntries.get(id); + if (existingEntry) { + existingEntry.dispose(); + } + + if (statusBarEntry.visible) { + this._cellStatusBarEntries.set( + id, + this.cellStatusBarService.addEntry(statusBarEntry)); + } + } } export class MainThreadNotebookKernel implements INotebookKernelInfo { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 864c337d345..2f4ce5afda9 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -992,6 +992,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I createConcatTextDocument(notebook, selector) { checkProposedApiEnabled(extension); return new ExtHostNotebookConcatDocument(extHostNotebook, extHostDocuments, notebook, selector); + }, + createCellStatusBarItem(cell: vscode.NotebookCell, alignment?: vscode.NotebookCellStatusBarAlignment, priority?: number): vscode.NotebookCellStatusBarItem { + checkProposedApiEnabled(extension); + return extHostNotebook.createNotebookCellStatusBarItemInternal(cell, alignment, priority); } }; @@ -1126,7 +1130,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I CellKind: extHostTypes.CellKind, CellOutputKind: extHostTypes.CellOutputKind, NotebookCellRunState: extHostTypes.NotebookCellRunState, - NotebookRunState: extHostTypes.NotebookRunState + NotebookRunState: extHostTypes.NotebookRunState, + NotebookCellStatusBarAlignment: extHostTypes.NotebookCellStatusBarAlignment }; }; } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 5156730c7f6..398a6fb71db 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -51,7 +51,7 @@ import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; import { TunnelOptions } from 'vs/platform/remote/common/tunnel'; import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline'; import { revive } from 'vs/base/common/marshalling'; -import { IProcessedOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEvent, NotebookDataDto, INotebookKernelInfoDto, IMainCellDto, INotebookDocumentFilter, INotebookKernelInfoDto2, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { IProcessedOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEvent, NotebookDataDto, INotebookKernelInfoDto, IMainCellDto, INotebookDocumentFilter, INotebookKernelInfoDto2, TransientMetadata, INotebookCellStatusBarEntry } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; import { Dto } from 'vs/base/common/types'; import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; @@ -719,6 +719,8 @@ export type NotebookCellOutputsSplice = [ IProcessedOutput[] ]; +export type INotebookCellStatusBarEntryDto = Dto; + export interface MainThreadNotebookShape extends IDisposable { $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, supportBackup: boolean, kernelInfoDto: INotebookKernelInfoDto | undefined, options: { transientOutputs: boolean; transientMetadata: TransientMetadata }): Promise; $onNotebookChange(viewType: string, resource: UriComponents): Promise; @@ -734,6 +736,7 @@ export interface MainThreadNotebookShape extends IDisposable { $updateNotebookCellMetadata(viewType: string, resource: UriComponents, handle: number, metadata: NotebookCellMetadata | undefined): Promise; $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[]): Promise; $postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise; + $setStatusBarEntry(id: number, statusBarEntry: INotebookCellStatusBarEntryDto): Promise; $onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void; $onContentChange(resource: UriComponents, viewType: string): void; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index bd8fad9da0e..8576ca499e1 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -14,15 +14,15 @@ import { ISplice } from 'vs/base/common/sequence'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as UUID from 'vs/base/common/uuid'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { CellKind, ExtHostNotebookShape, IMainContext, IModelAddedData, INotebookDocumentsAndEditorsDelta, INotebookEditorPropertiesChangeData, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice } from 'vs/workbench/api/common/extHost.protocol'; +import { CellKind, ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentsAndEditorsDelta, INotebookEditorPropertiesChangeData, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice } from 'vs/workbench/api/common/extHost.protocol'; import { ILogService } from 'vs/platform/log/common/log'; -import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; +import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; -import { CellEditType, CellOutputKind, diff, ICellDeleteEdit, ICellDto2, ICellEditOperation, ICellInsertEdit, IMainCellDto, INotebookDisplayOrder, INotebookEditData, INotebookKernelInfoDto2, IProcessedOutput, IRawOutput, NotebookCellMetadata, NotebookCellsChangedEvent, NotebookCellsChangeType, NotebookCellsSplice2, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, CellOutputKind, CellStatusbarAlignment, CellUri, diff, ICellDeleteEdit, ICellDto2, ICellEditOperation, ICellInsertEdit, IMainCellDto, INotebookCellStatusBarEntry, INotebookDisplayOrder, INotebookEditData, INotebookKernelInfoDto2, IProcessedOutput, IRawOutput, NotebookCellMetadata, NotebookCellsChangedEvent, NotebookCellsChangeType, NotebookCellsSplice2, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import * as vscode from 'vscode'; import { Cache } from './cache'; import { ResourceMap } from 'vs/base/common/map'; @@ -72,6 +72,9 @@ export class ExtHostCell extends Disposable { }; } + private _onDidDispose = new Emitter(); + readonly onDidDispose: Event = this._onDidDispose.event; + private _onDidChangeOutputs = new Emitter[]>(); readonly onDidChangeOutputs: Event[]> = this._onDidChangeOutputs.event; @@ -134,6 +137,11 @@ export class ExtHostCell extends Disposable { return this._cell; } + dispose() { + super.dispose(); + this._onDidDispose.fire(); + } + private _updateOutputs(newOutputs: vscode.CellOutput[]) { const rawDiffs = diff(this._outputs || [], newOutputs || [], (a) => { return this._outputMapping.has(a); @@ -345,7 +353,9 @@ export class ExtHostNotebookDocument extends Disposable { } if (!this._cellDisposableMapping.has(extCell.handle)) { - this._cellDisposableMapping.set(extCell.handle, new DisposableStore()); + const store = new DisposableStore(); + store.add(extCell); + this._cellDisposableMapping.set(extCell.handle, store); } const store = this._cellDisposableMapping.get(extCell.handle)!; @@ -880,6 +890,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN private readonly _unInitializedDocuments = new ResourceMap(); private readonly _editors = new Map(); private readonly _webviewComm = new Map(); + private readonly _commandsConverter: CommandsConverter; private readonly _onDidChangeNotebookCells = new Emitter(); readonly onDidChangeNotebookCells = this._onDidChangeNotebookCells.event; private readonly _onDidChangeCellOutputs = new Emitter(); @@ -928,6 +939,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN private readonly _extensionStoragePaths: IExtensionStoragePaths, ) { this._proxy = mainContext.getProxy(MainContext.MainThreadNotebook); + this._commandsConverter = commands.converter; commands.registerArgumentProcessor({ // Serialized INotebookCellActionContext @@ -1542,6 +1554,24 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN this._onDidChangeActiveNotebookEditor.fire(this._activeNotebookEditor); } } + + createNotebookCellStatusBarItemInternal(cell: vscode.NotebookCell, alignment: extHostTypes.NotebookCellStatusBarAlignment | undefined, priority: number | undefined) { + const statusBarItem = new NotebookCellStatusBarItemInternal(this._proxy, this._commandsConverter, cell, alignment, priority); + + // Look up the ExtHostCell for this NotebookCell URI, bind to its disposable lifecycle + const parsedUri = CellUri.parse(cell.uri); + if (parsedUri) { + const document = this._documents.get(parsedUri.notebook); + if (document) { + const cell = document.getCell(parsedUri.handle); + if (cell) { + Event.once(cell.onDidDispose)(() => statusBarItem.dispose()); + } + } + } + + return statusBarItem; + } } function hashPath(resource: URI): string { @@ -1553,3 +1583,178 @@ function isEditEvent(e: vscode.NotebookDocumentEditEvent | vscode.NotebookDocume return typeof (e as vscode.NotebookDocumentEditEvent).undo === 'function' && typeof (e as vscode.NotebookDocumentEditEvent).redo === 'function'; } + +export class NotebookCellStatusBarItemInternal extends Disposable { + private static NEXT_ID = 0; + + private readonly _id = NotebookCellStatusBarItemInternal.NEXT_ID++; + private readonly _internalCommandRegistration: DisposableStore; + + private _isDisposed = false; + private _alignment: extHostTypes.NotebookCellStatusBarAlignment; + + constructor( + private readonly _proxy: MainThreadNotebookShape, + private readonly _commands: CommandsConverter, + private readonly _cell: vscode.NotebookCell, + alignment: extHostTypes.NotebookCellStatusBarAlignment | undefined, + private _priority: number | undefined) { + super(); + this._internalCommandRegistration = this._register(new DisposableStore()); + this._alignment = alignment ?? extHostTypes.NotebookCellStatusBarAlignment.Left; + } + + private _apiItem: vscode.NotebookCellStatusBarItem | undefined; + get apiItem(): vscode.NotebookCellStatusBarItem { + if (!this._apiItem) { + this._apiItem = createNotebookCellStatusBarApiItem(this); + } + + return this._apiItem; + } + + get cell(): vscode.NotebookCell { + return this._cell; + } + + get alignment(): extHostTypes.NotebookCellStatusBarAlignment { + return this._alignment; + } + + set alignment(v: extHostTypes.NotebookCellStatusBarAlignment) { + this._alignment = v; + this.update(); + } + + get priority(): number | undefined { + return this._priority; + } + + set priority(v: number | undefined) { + this._priority = v; + this.update(); + } + + private _text: string = ''; + get text(): string { + return this._text; + } + + set text(v: string) { + this._text = v; + this.update(); + } + + private _tooltip: string | undefined; + get tooltip(): string | undefined { + return this._tooltip; + } + + set tooltip(v: string | undefined) { + this._tooltip = v; + this.update(); + } + + private _command?: { + readonly fromApi: string | vscode.Command, + readonly internal: ICommandDto, + }; + get command(): string | vscode.Command | undefined { + return this._command?.fromApi; + } + + set command(command: string | vscode.Command | undefined) { + if (this._command?.fromApi === command) { + return; + } + + this._internalCommandRegistration.clear(); + if (typeof command === 'string') { + this._command = { + fromApi: command, + internal: this._commands.toInternal({ title: '', command }, this._internalCommandRegistration), + }; + } else if (command) { + this._command = { + fromApi: command, + internal: this._commands.toInternal(command, this._internalCommandRegistration), + }; + } else { + this._command = undefined; + } + this.update(); + } + + private _accessibilityInformation: vscode.AccessibilityInformation | undefined; + get accessibilityInformation(): vscode.AccessibilityInformation | undefined { + return this._accessibilityInformation; + } + + set accessibilityInformation(v: vscode.AccessibilityInformation | undefined) { + this._accessibilityInformation = v; + this.update(); + } + + private _visible: boolean = false; + show(): void { + this._visible = true; + this.update(); + } + + hide(): void { + this._visible = false; + this.update(); + } + + dispose(): void { + this.hide(); + this._isDisposed = true; + this._internalCommandRegistration.dispose(); + } + + private update(): void { + if (this._isDisposed) { + return; + } + + const entry: INotebookCellStatusBarEntry = { + alignment: this.alignment === extHostTypes.NotebookCellStatusBarAlignment.Left ? CellStatusbarAlignment.LEFT : CellStatusbarAlignment.RIGHT, + cellResource: this.cell.uri, + command: this._command?.internal, + text: this.text, + tooltip: this.tooltip, + accessibilityInformation: this.accessibilityInformation, + priority: this.priority, + visible: this._visible + }; + + this._proxy.$setStatusBarEntry(this._id, entry); + } +} + +function createNotebookCellStatusBarApiItem(internalItem: NotebookCellStatusBarItemInternal): vscode.NotebookCellStatusBarItem { + return Object.freeze({ + cell: internalItem.cell, + get alignment() { return internalItem.alignment; }, + set alignment(v: NotebookCellStatusBarItemInternal['alignment']) { internalItem.alignment = v; }, + + get priority() { return internalItem.priority; }, + set priority(v: NotebookCellStatusBarItemInternal['priority']) { internalItem.priority = v; }, + + get text() { return internalItem.text; }, + set text(v: NotebookCellStatusBarItemInternal['text']) { internalItem.text = v; }, + + get tooltip() { return internalItem.tooltip; }, + set tooltip(v: NotebookCellStatusBarItemInternal['tooltip']) { internalItem.tooltip = v; }, + + get command() { return internalItem.command; }, + set command(v: NotebookCellStatusBarItemInternal['command']) { internalItem.command = v; }, + + get accessibilityInformation() { return internalItem.accessibilityInformation; }, + set accessibilityInformation(v: NotebookCellStatusBarItemInternal['accessibilityInformation']) { internalItem.accessibilityInformation = v; }, + + show() { internalItem.show(); }, + hide() { internalItem.hide(); }, + dispose() { internalItem.dispose(); } + }); +} diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 1ab689c1850..9fc497082bd 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2757,6 +2757,12 @@ export enum NotebookRunState { Idle = 2 } +export enum NotebookCellStatusBarAlignment { + Left = 1, + Right = 2 +} + + //#endregion //#region Timeline diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 87fc0a2edd7..f0e489a8675 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -362,11 +362,29 @@ .monaco-workbench .notebookOverlay .cell-statusbar-container .cell-status-right { padding-right: 12px; z-index: 26; + display: flex; +} + +.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-status-right .cell-contributed-items-right { + display: flex; + flex-wrap: wrap; + overflow: hidden; +} + +.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-status-item { + display: flex; + align-items: center; + white-space: pre; + + height: 21px; /* Editor outline is -1px in, don't overlap */ + padding: 0px 6px; +} + +.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-status-item.cell-status-item-has-command { + cursor: pointer; } .monaco-workbench .notebookOverlay .cell-statusbar-container .cell-language-picker { - height: 21px; /* Editor outline is -1px in, don't overlap */ - padding: 0px 6px; cursor: pointer; } @@ -380,6 +398,10 @@ align-items: center; } +.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-status-message { + margin-right: 6px; +} + .monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-status { height: 100%; display: flex; diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 0ad18223bc8..74df84fbeb1 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -46,6 +46,8 @@ import { NotebookDiffEditorInput } from 'vs/workbench/contrib/notebook/browser/n import { NotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor'; import { INotebookEditorWorkerService } from 'vs/workbench/contrib/notebook/common/services/notebookWorkerService'; import { NotebookEditorWorkerServiceImpl } from 'vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl'; +import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; +import { NotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/browser/notebookCellStatusBarServiceImpl'; // Editor Contribution @@ -445,6 +447,7 @@ workbenchContributionsRegistry.registerWorkbenchContribution(CellContentProvider registerSingleton(INotebookService, NotebookService); registerSingleton(INotebookEditorWorkerService, NotebookEditorWorkerServiceImpl); registerSingleton(INotebookEditorModelResolverService, NotebookModelResolverService, true); +registerSingleton(INotebookCellStatusBarService, NotebookCellStatusBarService, true); const configurationRegistry = Registry.as(Extensions.Configuration); configurationRegistry.registerConfiguration({ diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index d7c106c9531..a3b61443510 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -27,10 +27,10 @@ import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { IMenu } from 'vs/platform/actions/common/actions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { CellLanguageStatusBarItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents'; import { EditorOptions } from 'vs/workbench/common/editor'; import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; import { IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation'; +import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets'; export const KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED = new RawContextKey('notebookFindWidgetFocused', false); @@ -525,8 +525,7 @@ export interface BaseCellRenderTemplate { elementDisposables: DisposableStore; bottomCellContainer: HTMLElement; currentRenderedCell?: ICellViewModel; - statusBarContainer: HTMLElement; - languageStatusBarItem: CellLanguageStatusBarItem; + statusBar: CellEditorStatusBar; titleMenu: IMenu; toJSON: () => object; } @@ -539,7 +538,6 @@ export interface MarkdownCellRenderTemplate extends BaseCellRenderTemplate { export interface CodeCellRenderTemplate extends BaseCellRenderTemplate { cellRunState: RunStateRenderer; - cellStatusMessageContainer: HTMLElement; runToolbar: ToolBar; runButtonContainer: HTMLElement; executionOrderLabel: HTMLElement; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookCellStatusBarServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookCellStatusBarServiceImpl.ts new file mode 100644 index 00000000000..27abf1c095a --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/notebookCellStatusBarServiceImpl.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { ResourceMap } from 'vs/base/common/map'; +import { URI } from 'vs/base/common/uri'; +import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; +import { INotebookCellStatusBarEntry } from 'vs/workbench/contrib/notebook/common/notebookCommon'; + +export class NotebookCellStatusBarService extends Disposable implements INotebookCellStatusBarService { + + private _onDidChangeEntriesForCell = new Emitter(); + readonly onDidChangeEntriesForCell: Event = this._onDidChangeEntriesForCell.event; + + private _entries = new ResourceMap>(); + + private removeEntry(entry: INotebookCellStatusBarEntry) { + const existingEntries = this._entries.get(entry.cellResource); + if (existingEntries) { + existingEntries.delete(entry); + if (!existingEntries.size) { + this._entries.delete(entry.cellResource); + } + } + + this._onDidChangeEntriesForCell.fire(entry.cellResource); + } + + addEntry(entry: INotebookCellStatusBarEntry): IDisposable { + const existingEntries = this._entries.get(entry.cellResource) ?? new Set(); + existingEntries.add(entry); + this._entries.set(entry.cellResource, existingEntries); + + this._onDidChangeEntriesForCell.fire(entry.cellResource); + + return { + dispose: () => { + this.removeEntry(entry); + } + }; + } + + getEntries(cell: URI): INotebookCellStatusBarEntry[] { + const existingEntries = this._entries.get(cell); + return existingEntries ? + Array.from(existingEntries.values()) : + []; + } + + readonly _serviceBrand: undefined; +} diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index ffa9c892905..7bdc292f3bf 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1928,7 +1928,8 @@ registerThemingParticipant((theme, collector) => { const cellStatusBarHoverBg = theme.getColor(cellStatusBarItemHover); if (cellStatusBarHoverBg) { - collector.addRule(`.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-language-picker:hover { background-color: ${cellStatusBarHoverBg}; }`); + collector.addRule(`.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-language-picker:hover, + .monaco-workbench .notebookOverlay .cell-statusbar-container .cell-status-item.cell-status-item-has-command:hover { background-color: ${cellStatusBarHoverBg}; }`); } const cellInsertionIndicatorColor = theme.getColor(cellInsertionIndicator); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index f8b512e42cf..eb5c7a65cb1 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -40,15 +40,16 @@ import { CancelCellAction, DeleteCellAction, ExecuteCellAction, INotebookCellAct import { BaseCellRenderTemplate, CellEditState, CodeCellRenderTemplate, EXPAND_CELL_CONTENT_COMMAND_ID, ICellViewModel, INotebookEditor, isCodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys'; import { CellMenus } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellMenus'; +import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets'; import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/codeCell'; +import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents'; +import { CellDragAndDropController, DRAGGING_CLASS } from 'vs/workbench/contrib/notebook/browser/view/renderers/dnd'; import { StatefulMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/markdownCell'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { CellKind, NotebookCellMetadata, NotebookCellRunState, ShowCellStatusbarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { createAndFillInActionBarActionsWithVerticalSeparators, VerticalSeparator, VerticalSeparatorViewItem } from './cellActionView'; -import { CodiconActionViewItem, CellLanguageStatusBarItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents'; -import { CellDragAndDropController, DRAGGING_CLASS } from 'vs/workbench/contrib/notebook/browser/view/renderers/dnd'; const $ = DOM.$; @@ -403,7 +404,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR const bottomCellContainer = DOM.append(container, $('.cell-bottom-toolbar-container')); const betweenCellToolbar = disposables.add(this.createBetweenCellToolbar(bottomCellContainer, disposables, contextKeyService)); - const statusBar = this.instantiationService.createInstance(CellEditorStatusBar, editorPart); + const statusBar = disposables.add(this.instantiationService.createInstance(CellEditorStatusBar, editorPart)); const titleMenu = disposables.add(this.cellMenus.getCellTitleMenu(contextKeyService)); const templateData: MarkdownCellRenderTemplate = { @@ -422,9 +423,8 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR deleteToolbar, betweenCellToolbar, bottomCellContainer, - statusBarContainer: statusBar.statusBarContainer, - languageStatusBarItem: statusBar.languageStatusBarItem, titleMenu, + statusBar, toJSON: () => { return {}; } }; this.dndController.registerDragHandle(templateData, rootContainer, container, () => this.getDragImage(templateData)); @@ -493,7 +493,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR elementDisposables.add(this.editorOptions.onDidChange(newValue => markdownCell.updateEditorOptions(newValue))); elementDisposables.add(markdownCell); - templateData.languageStatusBarItem.update(element, this.notebookEditor); + templateData.statusBar.update(toolbarContext); } disposeTemplate(templateData: MarkdownCellRenderTemplate): void { @@ -605,27 +605,6 @@ class CodeCellDragImageRenderer { } } -class CellEditorStatusBar { - readonly cellStatusMessageContainer: HTMLElement; - readonly cellRunStatusContainer: HTMLElement; - readonly statusBarContainer: HTMLElement; - readonly languageStatusBarItem: CellLanguageStatusBarItem; - readonly durationContainer: HTMLElement; - - constructor( - container: HTMLElement, - @IInstantiationService instantiationService: IInstantiationService - ) { - this.statusBarContainer = DOM.append(container, $('.cell-statusbar-container')); - const leftStatusBarItems = DOM.append(this.statusBarContainer, $('.cell-status-left')); - const rightStatusBarItems = DOM.append(this.statusBarContainer, $('.cell-status-right')); - this.cellRunStatusContainer = DOM.append(leftStatusBarItems, $('.cell-run-status')); - this.durationContainer = DOM.append(leftStatusBarItems, $('.cell-run-duration')); - this.cellStatusMessageContainer = DOM.append(leftStatusBarItems, $('.cell-status-message')); - this.languageStatusBarItem = instantiationService.createInstance(CellLanguageStatusBarItem, rightStatusBarItems); - } -} - export class CodeCellRenderer extends AbstractCellRenderer implements IListRenderer { static readonly TEMPLATE_ID = 'code_cell'; @@ -692,7 +671,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende progressBar.hide(); disposables.add(progressBar); - const statusBar = this.instantiationService.createInstance(CellEditorStatusBar, editorPart); + const statusBar = disposables.add(this.instantiationService.createInstance(CellEditorStatusBar, editorPart)); const timer = new TimerRenderer(statusBar.durationContainer); const cellRunState = new RunStateRenderer(statusBar.cellRunStatusContainer, runToolbar, this.instantiationService); @@ -715,11 +694,9 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende contextKeyService, container, cellContainer, - statusBarContainer: statusBar.statusBarContainer, cellRunState, - cellStatusMessageContainer: statusBar.cellStatusMessageContainer, - languageStatusBarItem: statusBar.languageStatusBarItem, progressBar, + statusBar, focusIndicatorLeft: focusIndicator, focusIndicatorRight, focusIndicatorBottom, @@ -766,7 +743,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende const metadata = element.getEvaluatedMetadata(this.notebookEditor.viewModel!.notebookDocument.metadata); DOM.toggleClass(templateData.container, 'runnable', !!metadata.runnable); this.updateExecutionOrder(metadata, templateData); - templateData.cellStatusMessageContainer.textContent = metadata?.statusMessage || ''; + templateData.statusBar.cellStatusMessageContainer.textContent = metadata?.statusMessage || ''; templateData.cellRunState.renderState(element.metadata?.runState); @@ -880,7 +857,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende this.setBetweenCellToolbarContext(templateData, element, toolbarContext); - templateData.languageStatusBarItem.update(element, this.notebookEditor); + templateData.statusBar.update(toolbarContext); } disposeTemplate(templateData: CodeCellRenderTemplate): void { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts new file mode 100644 index 00000000000..9f86d687c07 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts @@ -0,0 +1,217 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as DOM from 'vs/base/browser/dom'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel'; +import { WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions'; +import { stripCodicons } from 'vs/base/common/codicons'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { extUri } from 'vs/base/common/resources'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { localize } from 'vs/nls'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ChangeCellLanguageAction, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; +import { ICellViewModel, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; +import { CellKind, CellStatusbarAlignment, INotebookCellStatusBarEntry } from 'vs/workbench/contrib/notebook/common/notebookCommon'; + +const $ = DOM.$; + +export class CellEditorStatusBar extends Disposable { + readonly cellStatusMessageContainer: HTMLElement; + readonly cellRunStatusContainer: HTMLElement; + readonly statusBarContainer: HTMLElement; + readonly languageStatusBarItem: CellLanguageStatusBarItem; + readonly durationContainer: HTMLElement; + + private readonly leftContributedItemsContainer: HTMLElement; + private readonly rightContributedItemsContainer: HTMLElement; + private readonly itemsDisposable: DisposableStore; + + private currentContext: INotebookCellActionContext | undefined; + + constructor( + container: HTMLElement, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @INotebookCellStatusBarService private readonly notebookCellStatusBarService: INotebookCellStatusBarService + ) { + super(); + this.statusBarContainer = DOM.append(container, $('.cell-statusbar-container')); + const leftItemsContainer = DOM.append(this.statusBarContainer, $('.cell-status-left')); + const rightItemsContainer = DOM.append(this.statusBarContainer, $('.cell-status-right')); + this.cellRunStatusContainer = DOM.append(leftItemsContainer, $('.cell-run-status')); + this.durationContainer = DOM.append(leftItemsContainer, $('.cell-run-duration')); + this.cellStatusMessageContainer = DOM.append(leftItemsContainer, $('.cell-status-message')); + this.leftContributedItemsContainer = DOM.append(leftItemsContainer, $('.cell-contributed-items-left')); + this.rightContributedItemsContainer = DOM.append(rightItemsContainer, $('.cell-contributed-items-right')); + this.languageStatusBarItem = instantiationService.createInstance(CellLanguageStatusBarItem, rightItemsContainer); + + this.itemsDisposable = this._register(new DisposableStore()); + this._register(this.notebookCellStatusBarService.onDidChangeEntriesForCell(e => { + if (this.currentContext && extUri.isEqual(e, this.currentContext.cell.uri)) { + this.updateStatusBarItems(); + } + })); + } + + update(context: INotebookCellActionContext) { + this.currentContext = context; + this.languageStatusBarItem.update(context.cell, context.notebookEditor); + this.updateStatusBarItems(); + } + + layout(width: number): void { + this.statusBarContainer.style.width = `${width}px`; + } + + private updateStatusBarItems() { + if (!this.currentContext) { + return; + } + + this.leftContributedItemsContainer.innerHTML = ''; + this.rightContributedItemsContainer.innerHTML = ''; + this.itemsDisposable.clear(); + + const items = this.notebookCellStatusBarService.getEntries(this.currentContext.cell.uri); + items.sort((itemA, itemB) => { + return (itemB.priority ?? 0) - (itemA.priority ?? 0); + }); + items.forEach(item => { + const itemView = this.itemsDisposable.add(this.instantiationService.createInstance(CellStatusBarItem, this.currentContext!, item)); + if (item.alignment === CellStatusbarAlignment.LEFT) { + this.leftContributedItemsContainer.appendChild(itemView.container); + } else { + this.rightContributedItemsContainer.appendChild(itemView.container); + } + }); + } +} + +class CellStatusBarItem extends Disposable { + + readonly container = $('.cell-status-item'); + + constructor( + private readonly _context: INotebookCellActionContext, + private readonly _itemModel: INotebookCellStatusBarEntry, + @ITelemetryService private readonly telemetryService: ITelemetryService, + @ICommandService private readonly commandService: ICommandService, + @INotificationService private readonly notificationService: INotificationService + ) { + super(); + new CodiconLabel(this.container).text = this._itemModel.text; + + let ariaLabel: string; + let role: string | undefined; + if (this._itemModel.accessibilityInformation) { + ariaLabel = this._itemModel.accessibilityInformation.label; + role = this._itemModel.accessibilityInformation.role; + } else { + ariaLabel = this._itemModel.text ? stripCodicons(this._itemModel.text).trim() : ''; + } + + if (ariaLabel) { + this.container.setAttribute('aria-label', ariaLabel); + } + + if (role) { + this.container.setAttribute('role', role); + } + + this.container.title = this._itemModel.tooltip ?? ''; + + if (this._itemModel.command) { + this.container.classList.add('cell-status-item-has-command'); + this.container.tabIndex = 0; + + this._register(DOM.addDisposableListener(this.container, DOM.EventType.CLICK, _e => { + this.executeCommand(); + })); + this._register(DOM.addDisposableListener(this.container, DOM.EventType.KEY_UP, e => { + const event = new StandardKeyboardEvent(e); + if (event.equals(KeyCode.Space) || event.equals(KeyCode.Enter)) { + this.executeCommand(); + } + })); + } + } + + private async executeCommand(): Promise { + const command = this._itemModel.command; + if (!command) { + return; + } + + const id = typeof command === 'string' ? command : command.id; + const args = typeof command === 'string' ? [] : command.arguments ?? []; + + args.unshift(this._context); + + this.telemetryService.publicLog2('workbenchActionExecuted', { id, from: 'cell status bar' }); + try { + await this.commandService.executeCommand(id, ...args); + } catch (error) { + this.notificationService.error(toErrorMessage(error)); + } + } +} + +export class CellLanguageStatusBarItem extends Disposable { + private readonly labelElement: HTMLElement; + + private cell: ICellViewModel | undefined; + private editor: INotebookEditor | undefined; + + private cellDisposables: DisposableStore; + + constructor( + readonly container: HTMLElement, + @IModeService private readonly modeService: IModeService, + @IInstantiationService private readonly instantiationService: IInstantiationService + ) { + super(); + this.labelElement = DOM.append(container, $('.cell-language-picker.cell-status-item')); + this.labelElement.tabIndex = 0; + + this._register(DOM.addDisposableListener(this.labelElement, DOM.EventType.CLICK, () => { + this.run(); + })); + this._register(DOM.addDisposableListener(this.labelElement, DOM.EventType.KEY_UP, e => { + const event = new StandardKeyboardEvent(e); + if (event.equals(KeyCode.Space) || event.equals(KeyCode.Enter)) { + this.run(); + } + })); + this._register(this.cellDisposables = new DisposableStore()); + } + + private run() { + this.instantiationService.invokeFunction(accessor => { + new ChangeCellLanguageAction().run(accessor, { notebookEditor: this.editor!, cell: this.cell! }); + }); + } + + update(cell: ICellViewModel, editor: INotebookEditor): void { + this.cellDisposables.clear(); + this.cell = cell; + this.editor = editor; + + this.render(); + this.cellDisposables.add(this.cell.model.onDidChangeLanguage(() => this.render())); + } + + private render(): void { + const modeId = this.cell?.cellKind === CellKind.Markdown ? 'markdown' : this.modeService.getModeIdForLanguageName(this.cell!.language) || this.cell!.language; + this.labelElement.textContent = this.modeService.getLanguageName(modeId) || this.modeService.getLanguageName('plaintext'); + this.labelElement.title = localize('notebook.cell.status.language', "Select Cell Language Mode"); + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index 1c0c22f4482..524f3326aaf 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -401,7 +401,7 @@ export class CodeCell extends Disposable { private layoutEditor(dimension: IDimension): void { this.templateData.editor?.layout(dimension); - this.templateData.statusBarContainer.style.width = `${dimension.width}px`; + this.templateData.statusBar.layout(dimension.width); } private onCellWidthChange(): void { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents.ts index 01db1c49f46..989f392ea4c 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents.ts @@ -3,22 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { localize } from 'vs/nls'; -import * as DOM from 'vs/base/browser/dom'; +import { renderCodicons } from 'vs/base/common/codicons'; import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { MenuItemAction } from 'vs/platform/actions/common/actions'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { renderCodicons } from 'vs/base/common/codicons'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { ICellViewModel, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { IModeService } from 'vs/editor/common/services/modeService'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ChangeCellLanguageAction } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; -import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; - -const $ = DOM.$; export class CodiconActionViewItem extends MenuEntryActionViewItem { constructor( @@ -35,45 +25,3 @@ export class CodiconActionViewItem extends MenuEntryActionViewItem { } } } - - -export class CellLanguageStatusBarItem extends Disposable { - private readonly labelElement: HTMLElement; - - private cell: ICellViewModel | undefined; - private editor: INotebookEditor | undefined; - - private cellDisposables: DisposableStore; - - constructor( - readonly container: HTMLElement, - @IModeService private readonly modeService: IModeService, - @IInstantiationService private readonly instantiationService: IInstantiationService - ) { - super(); - this.labelElement = DOM.append(container, $('.cell-language-picker')); - this.labelElement.tabIndex = 0; - - this._register(DOM.addDisposableListener(this.labelElement, DOM.EventType.CLICK, () => { - this.instantiationService.invokeFunction(accessor => { - new ChangeCellLanguageAction().run(accessor, { notebookEditor: this.editor!, cell: this.cell! }); - }); - })); - this._register(this.cellDisposables = new DisposableStore()); - } - - update(cell: ICellViewModel, editor: INotebookEditor): void { - this.cellDisposables.clear(); - this.cell = cell; - this.editor = editor; - - this.render(); - this.cellDisposables.add(this.cell.model.onDidChangeLanguage(() => this.render())); - } - - private render(): void { - const modeId = this.cell?.cellKind === CellKind.Markdown ? 'markdown' : this.modeService.getModeIdForLanguageName(this.cell!.language) || this.cell!.language; - this.labelElement.textContent = this.modeService.getLanguageName(modeId) || this.modeService.getLanguageName('plaintext'); - this.labelElement.title = localize('notebook.cell.status.language', "Select Cell Language Mode"); - } -} diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts index 260889e19b9..a1e81ae72e5 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts @@ -282,7 +282,7 @@ export class StatefulMarkdownCell extends Disposable { private layoutEditor(dimension: DOM.IDimension): void { this.editor?.layout(dimension); - this.templateData.statusBarContainer.style.width = `${dimension.width}px`; + this.templateData.statusBar.layout(dimension.width); } private onCellEditorWidthChange(): void { diff --git a/src/vs/workbench/contrib/notebook/common/notebookCellStatusBarService.ts b/src/vs/workbench/contrib/notebook/common/notebookCellStatusBarService.ts new file mode 100644 index 00000000000..e99fa061a11 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/common/notebookCellStatusBarService.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. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from 'vs/base/common/event'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { INotebookCellStatusBarEntry } from 'vs/workbench/contrib/notebook/common/notebookCommon'; + +export const INotebookCellStatusBarService = createDecorator('notebookCellStatusBarService'); + +export interface INotebookCellStatusBarService { + readonly _serviceBrand: undefined; + + onDidChangeEntriesForCell: Event; + + addEntry(entry: INotebookCellStatusBarEntry): IDisposable; + getEntries(cell: URI): INotebookCellStatusBarEntry[]; +} diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 81487436421..2e983236512 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -3,22 +3,24 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IDiffResult, ISequence } from 'vs/base/common/diff/diff'; import { Event } from 'vs/base/common/event'; import * as glob from 'vs/base/common/glob'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; +import { basename } from 'vs/base/common/path'; import { isWindows } from 'vs/base/common/platform'; import { ISplice } from 'vs/base/common/sequence'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { Command } from 'vs/editor/common/modes'; +import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IEditorModel } from 'vs/platform/editor/common/editor'; -import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { Schemas } from 'vs/base/common/network'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IRevertOptions } from 'vs/workbench/common/editor'; -import { basename } from 'vs/base/common/path'; -import { IDiffResult, ISequence } from 'vs/base/common/diff/diff'; +import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; export enum CellKind { Markdown = 1, @@ -744,7 +746,6 @@ export interface INotebookKernelProvider { cancelNotebook(uri: URI, kernelId: string, handle: number | undefined): Promise; } - export class CellSequence implements ISequence { constructor(readonly textModel: NotebookTextModel) { @@ -764,6 +765,23 @@ export interface INotebookDiffResult { cellsDiff: IDiffResult, linesDiff?: { originalCellhandle: number, modifiedCellhandle: number, lineChanges: editorCommon.ILineChange[] }[]; } + +export interface INotebookCellStatusBarEntry { + readonly cellResource: URI; + readonly alignment: CellStatusbarAlignment; + readonly priority?: number; + readonly text: string; + readonly tooltip: string | undefined; + readonly command: string | Command | undefined; + readonly accessibilityInformation?: IAccessibilityInformation; + readonly visible: boolean; +} + export const DisplayOrderKey = 'notebook.displayOrder'; export const CellToolbarLocKey = 'notebook.cellToolbarLocation'; export const ShowCellStatusbarKey = 'notebook.showCellStatusbar'; + +export const enum CellStatusbarAlignment { + LEFT, + RIGHT +} From 5a968d0235bfc975d7bf3e5dbdcb0f06222c0d5c Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Wed, 26 Aug 2020 10:45:40 -0400 Subject: [PATCH 582/736] Adds scmProviderRootUri context key --- src/vs/workbench/contrib/scm/browser/menus.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/scm/browser/menus.ts b/src/vs/workbench/contrib/scm/browser/menus.ts index aff10c40551..4fd31d3a776 100644 --- a/src/vs/workbench/contrib/scm/browser/menus.ts +++ b/src/vs/workbench/contrib/scm/browser/menus.ts @@ -180,6 +180,7 @@ export class SCMRepositoryMenus implements ISCMRepositoryMenus, IDisposable { ) { this.contextKeyService = contextKeyService.createScoped(); this.contextKeyService.createKey('scmProvider', provider.contextValue); + this.contextKeyService.createKey('scmProviderRootUri', provider.rootUri?.toString()); this.contextKeyService.createKey('scmProviderHasRootUri', !!provider.rootUri); const serviceCollection = new ServiceCollection([IContextKeyService, this.contextKeyService]); From 7fc5375536c70db3c160f7c1399eafb780e72fef Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 27 Aug 2020 09:45:38 +0200 Subject: [PATCH 583/736] Language selector shows incorrect Icons for Javascript. Fixes #105244 --- .../editor/common/services/getIconClasses.ts | 5 +++ .../browser/parts/editor/editorStatus.ts | 43 ++++++------------- 2 files changed, 18 insertions(+), 30 deletions(-) diff --git a/src/vs/editor/common/services/getIconClasses.ts b/src/vs/editor/common/services/getIconClasses.ts index fbdc31d6d5c..b20a0b7c58b 100644 --- a/src/vs/editor/common/services/getIconClasses.ts +++ b/src/vs/editor/common/services/getIconClasses.ts @@ -54,6 +54,11 @@ export function getIconClasses(modelService: IModelService, modeService: IModeSe return classes; } + +export function getIconClassesForModeId(modeId: string): string[] { + return ['file-icon', `${cssEscape(modeId)}-lang-file-icon`]; +} + export function detectModeId(modelService: IModelService, modeService: IModeService, resource: uri): string | null { if (!resource) { return null; // we need a resource at least diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 14dca63bc68..cc847e3c1e2 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -26,7 +26,6 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IFileService, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; -import { IModelService } from 'vs/editor/common/services/modelService'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { TabFocus } from 'vs/editor/common/config/commonEditorConfig'; @@ -43,7 +42,7 @@ import { ICodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser'; import { Schemas } from 'vs/base/common/network'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; -import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; +import { getIconClassesForModeId } from 'vs/editor/common/services/getIconClasses'; import { timeout } from 'vs/base/common/async'; import { INotificationHandle, INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { Event } from 'vs/base/common/event'; @@ -1042,7 +1041,6 @@ export class ChangeModeAction extends Action { actionId: string, actionLabel: string, @IModeService private readonly modeService: IModeService, - @IModelService private readonly modelService: IModelService, @IEditorService private readonly editorService: IEditorService, @IConfigurationService private readonly configurationService: IConfigurationService, @IQuickInputService private readonly quickInputService: IQuickInputService, @@ -1069,26 +1067,27 @@ export class ChangeModeAction extends Action { } // Compute mode + let currentLanguageId: string | undefined; let currentModeId: string | undefined; - let modeId: string | undefined; if (textModel) { - modeId = textModel.getLanguageIdentifier().language; - currentModeId = withNullAsUndefined(this.modeService.getLanguageName(modeId)); + currentModeId = textModel.getLanguageIdentifier().language; + currentLanguageId = withNullAsUndefined(this.modeService.getLanguageName(currentModeId)); } // All languages are valid picks const languages = this.modeService.getRegisteredLanguageNames(); const picks: QuickPickInput[] = languages.sort().map((lang, index) => { + const modeId = this.modeService.getModeIdForLanguageName(lang.toLowerCase()) || 'unknown'; let description: string; - if (currentModeId === lang) { - description = nls.localize('languageDescription', "({0}) - Configured Language", this.modeService.getModeIdForLanguageName(lang.toLowerCase())); + if (currentLanguageId === lang) { + description = nls.localize('languageDescription', "({0}) - Configured Language", modeId); } else { - description = nls.localize('languageDescriptionConfigured', "({0})", this.modeService.getModeIdForLanguageName(lang.toLowerCase())); + description = nls.localize('languageDescriptionConfigured', "({0})", modeId); } return { label: lang, - iconClasses: getIconClasses(this.modelService, this.modeService, this.getFakeResource(lang)), + iconClasses: getIconClassesForModeId(modeId), description }; }); @@ -1109,7 +1108,7 @@ export class ChangeModeAction extends Action { picks.unshift(galleryAction); } - configureModeSettings = { label: nls.localize('configureModeSettings', "Configure '{0}' language based settings...", currentModeId) }; + configureModeSettings = { label: nls.localize('configureModeSettings', "Configure '{0}' language based settings...", currentLanguageId) }; picks.unshift(configureModeSettings); configureModeAssociations = { label: nls.localize('configureAssociationsExt', "Configure File Association for '{0}'...", ext) }; picks.unshift(configureModeAssociations); @@ -1144,7 +1143,7 @@ export class ChangeModeAction extends Action { // User decided to configure settings for current language if (pick === configureModeSettings) { - this.preferencesService.openGlobalSettings(true, { editSetting: `[${withUndefinedAsNull(modeId)}]` }); + this.preferencesService.openGlobalSettings(true, { editSetting: `[${withUndefinedAsNull(currentModeId)}]` }); return; } @@ -1182,12 +1181,12 @@ export class ChangeModeAction extends Action { const languages = this.modeService.getRegisteredLanguageNames(); const picks: IQuickPickItem[] = languages.sort().map((lang, index) => { - const id = withNullAsUndefined(this.modeService.getModeIdForLanguageName(lang.toLowerCase())); + const id = withNullAsUndefined(this.modeService.getModeIdForLanguageName(lang.toLowerCase())) || 'unknown'; return { id, label: lang, - iconClasses: getIconClasses(this.modelService, this.modeService, this.getFakeResource(lang)), + iconClasses: getIconClassesForModeId(id), description: (id === currentAssociation) ? nls.localize('currentAssociation', "Current Association") : undefined }; }); @@ -1218,22 +1217,6 @@ export class ChangeModeAction extends Action { } }, 50 /* quick input is sensitive to being opened so soon after another */); } - - private getFakeResource(lang: string): URI | undefined { - let fakeResource: URI | undefined; - - const extensions = this.modeService.getExtensions(lang); - if (extensions?.length) { - fakeResource = URI.file(extensions[0]); - } else { - const filenames = this.modeService.getFilenames(lang); - if (filenames?.length) { - fakeResource = URI.file(filenames[0]); - } - } - - return fakeResource; - } } export interface IChangeEOLEntry extends IQuickPickItem { From f4d92fcb734cb6be2db21249b4916c547af8794c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 27 Aug 2020 10:24:50 +0200 Subject: [PATCH 584/736] electron 9: adopt 'render-process-gone' event and show details --- src/vs/code/electron-main/window.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index bf55feac2e5..bbe732c34be 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -8,7 +8,7 @@ import * as objects from 'vs/base/common/objects'; import * as nls from 'vs/nls'; import { Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; -import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment, nativeTheme, Event } from 'electron'; +import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment, nativeTheme, Event, Details } from 'electron'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { ILogService } from 'vs/platform/log/common/log'; @@ -403,7 +403,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { private registerListeners(): void { // Crashes & Unrsponsive - this._win.webContents.on('crashed', () => this.onWindowError(WindowError.CRASHED)); + this._win.webContents.on('render-process-gone', (event, details) => this.onWindowError(WindowError.CRASHED, details)); this._win.on('unresponsive', () => this.onWindowError(WindowError.UNRESPONSIVE)); // Window close @@ -535,8 +535,10 @@ export class CodeWindow extends Disposable implements ICodeWindow { this.marketplaceHeadersPromise.then(headers => cb({ cancel: false, requestHeaders: Object.assign(details.requestHeaders, headers) }))); } - private onWindowError(error: WindowError): void { - this.logService.error(error === WindowError.CRASHED ? '[VS Code]: renderer process crashed!' : '[VS Code]: detected unresponsive'); + private onWindowError(error: WindowError.UNRESPONSIVE): void; + private onWindowError(error: WindowError.CRASHED, details: Details): void; + private onWindowError(error: WindowError, details?: Details): void { + this.logService.error(error === WindowError.CRASHED ? `[VS Code]: renderer process crashed (detail: ${details?.reason})` : '[VS Code]: detected unresponsive'); // If we run extension tests from CLI, showing a dialog is not // very helpful in this case. Rather, we bring down the test run @@ -590,11 +592,18 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Crashed else { + let message: string; + if (details && details.reason !== 'crashed') { + message = nls.localize('appCrashedDetails', "The window has crashed (reason: '{0}')", details?.reason); + } else { + message = nls.localize('appCrashed', "The window has crashed", details?.reason); + } + this.dialogMainService.showMessageBox({ title: product.nameLong, type: 'warning', buttons: [mnemonicButtonLabel(nls.localize({ key: 'reopen', comment: ['&& denotes a mnemonic'] }, "&&Reopen")), mnemonicButtonLabel(nls.localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close"))], - message: nls.localize('appCrashed', "The window has crashed"), + message, detail: nls.localize('appCrashedDetail', "We are sorry for the inconvenience! You can reopen the window to continue where you left off."), noLink: true }, this._win).then(result => { From 860a68747f1ca4cafcc3c96309b468e85ff89382 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 27 Aug 2020 10:18:14 +0200 Subject: [PATCH 585/736] WorkspaceEdit#replaceCells|CellOutput|CellMetadata, https://github.com/microsoft/vscode/issues/105283 --- src/vs/vscode.proposed.d.ts | 6 ++++ .../api/browser/mainThreadEditors.ts | 3 ++ .../workbench/api/common/extHost.protocol.ts | 11 ++++++- .../common/extHostFileSystemEventService.ts | 12 ++++---- .../workbench/api/common/extHostNotebook.ts | 5 +--- .../api/common/extHostTypeConverters.ts | 12 ++++++-- src/vs/workbench/api/common/extHostTypes.ts | 30 +++++++++++++++++-- .../contrib/notebook/common/notebookCommon.ts | 8 ++++- .../test/browser/api/extHostTypes.test.ts | 6 ++-- 9 files changed, 72 insertions(+), 21 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index ba6a5453aac..7358ec83ed8 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1335,6 +1335,12 @@ declare module 'vscode' { contains(uri: Uri): boolean } + export interface WorkspaceEdit { + replaceCells(uri: Uri, start: number, end: number, cells: NotebookCellData[], metadata?: WorkspaceEditEntryMetadata): void; + replaceCellOutput(uri: Uri, index: number, outputs: CellOutput[], metadata?: WorkspaceEditEntryMetadata): void; + replaceCellMetadata(uri: Uri, index: number, cellMetadata: NotebookCellMetadata, metadata?: WorkspaceEditEntryMetadata): void; + } + export interface NotebookEditorCellEdit { replaceCells(from: number, to: number, cells: NotebookCellData[]): void; diff --git a/src/vs/workbench/api/browser/mainThreadEditors.ts b/src/vs/workbench/api/browser/mainThreadEditors.ts index 6ea3c01a77c..d8e199ac0ad 100644 --- a/src/vs/workbench/api/browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadEditors.ts @@ -30,6 +30,7 @@ import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWi import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { revive } from 'vs/base/common/marshalling'; +import { ResourceNotebookCellEdit } from 'vs/workbench/contrib/bulkEdit/browser/bulkCellEdits'; function reviveWorkspaceEditDto2(data: IWorkspaceEditDto | undefined): ResourceEdit[] { if (!data?.edits) { @@ -42,6 +43,8 @@ function reviveWorkspaceEditDto2(data: IWorkspaceEditDto | undefined): ResourceE result.push(new ResourceFileEdit(edit.oldUri, edit.newUri, edit.options, edit.metadata)); } else if (edit._type === WorkspaceEditType.Text) { result.push(new ResourceTextEdit(edit.resource, edit.edit, edit.modelVersionId, edit.metadata)); + } else if (edit._type === WorkspaceEditType.Cell) { + result.push(new ResourceNotebookCellEdit(edit.resource, edit.edit, edit.modelVersionId, edit.metadata)); } } return result; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 398a6fb71db..6b001f1df78 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1243,6 +1243,7 @@ export interface IWorkspaceEditEntryMetadataDto { export const enum WorkspaceEditType { File = 1, Text = 2, + Cell = 3, } export interface IWorkspaceFileEditDto { @@ -1261,8 +1262,16 @@ export interface IWorkspaceTextEditDto { metadata?: IWorkspaceEditEntryMetadataDto; } +export interface IWorkspaceCellEditDto { + _type: WorkspaceEditType.Cell; + resource: UriComponents; + edit: ICellEditOperation; + modelVersionId?: number; + metadata?: IWorkspaceEditEntryMetadataDto; +} + export interface IWorkspaceEditDto { - edits: Array; + edits: Array; // todo@joh reject should go into rename rejectReason?: string; diff --git a/src/vs/workbench/api/common/extHostFileSystemEventService.ts b/src/vs/workbench/api/common/extHostFileSystemEventService.ts index 41606d40393..76a5f2f81e9 100644 --- a/src/vs/workbench/api/common/extHostFileSystemEventService.ts +++ b/src/vs/workbench/api/common/extHostFileSystemEventService.ts @@ -8,12 +8,11 @@ import { IRelativePattern, parse } from 'vs/base/common/glob'; import { URI } from 'vs/base/common/uri'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import type * as vscode from 'vscode'; -import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, MainContext, MainThreadTextEditorsShape, IWorkspaceFileEditDto, IWorkspaceTextEditDto, SourceTargetPair } from './extHost.protocol'; +import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, MainContext, MainThreadTextEditorsShape, SourceTargetPair, IWorkspaceEditDto } from './extHost.protocol'; import * as typeConverter from './extHostTypeConverters'; import { Disposable, WorkspaceEdit } from './extHostTypes'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { FileOperation } from 'vs/platform/files/common/files'; -import { flatten } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ILogService } from 'vs/platform/log/common/log'; @@ -217,14 +216,13 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ } if (edits.length > 0) { - // flatten all WorkspaceEdits collected via waitUntil-call - // and apply them in one go. - const allEdits = new Array>(); + // concat all WorkspaceEdits collected via waitUntil-call and apply them in one go. + const dto: IWorkspaceEditDto = { edits: [] }; for (let edit of edits) { let { edits } = typeConverter.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors); - allEdits.push(edits); + dto.edits = dto.edits.concat(edits); } - return this._mainThreadTextEditors.$tryApplyWorkspaceEdit({ edits: flatten(allEdits) }); + return this._mainThreadTextEditors.$tryApplyWorkspaceEdit(dto); } } } diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 8576ca499e1..8a301cc9168 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -22,7 +22,7 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; -import { CellEditType, CellOutputKind, CellStatusbarAlignment, CellUri, diff, ICellDeleteEdit, ICellDto2, ICellEditOperation, ICellInsertEdit, IMainCellDto, INotebookCellStatusBarEntry, INotebookDisplayOrder, INotebookEditData, INotebookKernelInfoDto2, IProcessedOutput, IRawOutput, NotebookCellMetadata, NotebookCellsChangedEvent, NotebookCellsChangeType, NotebookCellsSplice2, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { addIdToOutput, CellEditType, CellOutputKind, CellStatusbarAlignment, CellUri, diff, ICellDeleteEdit, ICellDto2, ICellEditOperation, ICellInsertEdit, IMainCellDto, INotebookCellStatusBarEntry, INotebookDisplayOrder, INotebookEditData, INotebookKernelInfoDto2, IProcessedOutput, NotebookCellMetadata, NotebookCellsChangedEvent, NotebookCellsChangeType, NotebookCellsSplice2, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import * as vscode from 'vscode'; import { Cache } from './cache'; import { ResourceMap } from 'vs/base/common/map'; @@ -55,9 +55,6 @@ interface INotebookEventEmitter { emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void; } -const addIdToOutput = (output: IRawOutput, id = UUID.generateUuid()): IProcessedOutput => output.outputKind === CellOutputKind.Rich - ? ({ ...output, outputId: id }) : output; - export class ExtHostCell extends Disposable { public static asModelAddData(notebook: vscode.NotebookDocument, cell: IMainCellDto): IExtHostModelAddedData { diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index c5712ebba6d..b09d7952069 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -511,7 +511,7 @@ export namespace WorkspaceEdit { }; if (value instanceof types.WorkspaceEdit) { - for (let entry of value.allEntries()) { + for (let entry of value._allEntries()) { if (entry._type === types.FileEditType.File) { // file operation @@ -523,7 +523,7 @@ export namespace WorkspaceEdit { metadata: entry.metadata }); - } else { + } else if (entry._type === types.FileEditType.Text) { // text edits const doc = documents?.getDocument(entry.uri); result.edits.push({ @@ -533,6 +533,14 @@ export namespace WorkspaceEdit { modelVersionId: doc?.version, metadata: entry.metadata }); + } else if (entry._type === types.FileEditType.Cell) { + result.edits.push({ + _type: extHostProtocol.WorkspaceEditType.Cell, + resource: entry.uri, + edit: entry.edit, + metadata: entry.metadata, + modelVersionId: undefined, // todo@jrieken + }); } } } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 9fc497082bd..905debce535 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -15,6 +15,7 @@ import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/platform/files/common/files'; import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { addIdToOutput, CellEditType, ICellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import type * as vscode from 'vscode'; function es5ClassCompat(target: Function): any { @@ -578,7 +579,8 @@ export interface IFileOperationOptions { export const enum FileEditType { File = 1, - Text = 2 + Text = 2, + Cell = 3 } export interface IFileOperation { @@ -596,13 +598,20 @@ export interface IFileTextEdit { metadata?: vscode.WorkspaceEditEntryMetadata; } +export interface IFileCellEdit { + _type: FileEditType.Cell; + uri: URI; + edit: ICellEditOperation; + metadata?: vscode.WorkspaceEditEntryMetadata; +} + @es5ClassCompat export class WorkspaceEdit implements vscode.WorkspaceEdit { - private readonly _edits = new Array(); + private readonly _edits = new Array(); - allEntries(): ReadonlyArray { + _allEntries(): ReadonlyArray { return this._edits; } @@ -620,6 +629,21 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { this._edits.push({ _type: FileEditType.File, from: uri, to: undefined, options, metadata }); } + // --- cell + + replaceCells(uri: URI, start: number, end: number, cells: vscode.NotebookCellData[], metadata?: vscode.WorkspaceEditEntryMetadata): void { + this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.Delete, index: start, count: end - start } }); + this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.Insert, index: start, cells: cells.map(cell => ({ ...cell, outputs: cell.outputs.map(output => addIdToOutput(output)) })) } }); + } + + replaceCellOutput(uri: URI, index: number, outputs: vscode.CellOutput[], metadata?: vscode.WorkspaceEditEntryMetadata): void { + this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.Output, index, outputs: outputs.map(output => addIdToOutput(output)) } }); + } + + replaceCellMetadata(uri: URI, index: number, cellMetadata: vscode.NotebookCellMetadata, metadata?: vscode.WorkspaceEditEntryMetadata): void { + this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.Metadata, index, metadata: cellMetadata } }); + } + // --- text replace(uri: URI, range: Range, newText: string, metadata?: vscode.WorkspaceEditEntryMetadata): void { diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 2e983236512..fcb70b1599c 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -7,7 +7,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IDiffResult, ISequence } from 'vs/base/common/diff/diff'; import { Event } from 'vs/base/common/event'; import * as glob from 'vs/base/common/glob'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import * as UUID from 'vs/base/common/uuid'; import { Schemas } from 'vs/base/common/network'; import { basename } from 'vs/base/common/path'; import { isWindows } from 'vs/base/common/platform'; @@ -21,6 +21,7 @@ import { IEditorModel } from 'vs/platform/editor/common/editor'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IRevertOptions } from 'vs/workbench/common/editor'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; +import { IDisposable } from 'vs/base/common/lifecycle'; export enum CellKind { Markdown = 1, @@ -221,6 +222,11 @@ export interface IGenericOutput { transformedOutput?: { [key: string]: IDisplayOutput }; } + +export const addIdToOutput = (output: IRawOutput, id = UUID.generateUuid()): IProcessedOutput => output.outputKind === CellOutputKind.Rich + ? ({ ...output, outputId: id }) : output; + + export type IProcessedOutput = ITransformedDisplayOutputDto | IStreamOutput | IErrorOutput; export type IRawOutput = IDisplayOutput | IStreamOutput | IErrorOutput; diff --git a/src/vs/workbench/test/browser/api/extHostTypes.test.ts b/src/vs/workbench/test/browser/api/extHostTypes.test.ts index fe6998e08c6..08e6159df75 100644 --- a/src/vs/workbench/test/browser/api/extHostTypes.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTypes.test.ts @@ -384,7 +384,7 @@ suite('ExtHostTypes', function () { edit.replace(URI.parse('foo:a'), new types.Range(2, 1, 2, 1), 'bar'); edit.replace(URI.parse('foo:b'), new types.Range(3, 1, 3, 1), 'bazz'); - const all = edit.allEntries(); + const all = edit._allEntries(); assert.equal(all.length, 4); const [first, second, third, fourth] = all; @@ -408,8 +408,8 @@ suite('ExtHostTypes', function () { edit.insert(uri, new types.Position(0, 0), 'Hello'); edit.insert(uri, new types.Position(0, 0), 'Foo'); - assert.equal(edit.allEntries().length, 2); - let [first, second] = edit.allEntries(); + assert.equal(edit._allEntries().length, 2); + let [first, second] = edit._allEntries(); assertType(first._type === types.FileEditType.Text); assertType(second._type === types.FileEditType.Text); From 21dd836ddbdb2772fa478593e58ed827aa9d1766 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 27 Aug 2020 10:44:55 +0200 Subject: [PATCH 586/736] macOS: detect high contrast theme (fix #105481) --- src/vs/code/electron-main/window.ts | 2 +- src/vs/platform/theme/electron-main/themeMainService.ts | 4 ++-- src/vs/platform/windows/electron-main/windowsMainService.ts | 4 ++-- src/vs/workbench/electron-sandbox/desktop.contribution.ts | 4 ++-- src/vs/workbench/services/themes/common/themeConfiguration.ts | 3 ++- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index bbe732c34be..48fde1abec3 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -778,7 +778,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { if (windowConfig?.autoDetectHighContrast === false) { autoDetectHighContrast = false; } - windowConfiguration.highContrast = isWindows && autoDetectHighContrast && nativeTheme.shouldUseInvertedColorScheme; + windowConfiguration.highContrast = (isWindows || isMacintosh) && autoDetectHighContrast && nativeTheme.shouldUseInvertedColorScheme; windowConfiguration.accessibilitySupport = app.accessibilitySupportEnabled; // Title style related diff --git a/src/vs/platform/theme/electron-main/themeMainService.ts b/src/vs/platform/theme/electron-main/themeMainService.ts index 0d16ddb9002..7bbacdb3d52 100644 --- a/src/vs/platform/theme/electron-main/themeMainService.ts +++ b/src/vs/platform/theme/electron-main/themeMainService.ts @@ -42,14 +42,14 @@ export class ThemeMainService implements IThemeMainService { } getBackgroundColor(): string { - if (isWindows && nativeTheme.shouldUseInvertedColorScheme) { + if ((isWindows || isMacintosh) && nativeTheme.shouldUseInvertedColorScheme) { return DEFAULT_BG_HC_BLACK; } let background = this.stateService.getItem(THEME_BG_STORAGE_KEY, null); if (!background) { let baseTheme: string; - if (isWindows && nativeTheme.shouldUseInvertedColorScheme) { + if ((isWindows || isMacintosh) && nativeTheme.shouldUseInvertedColorScheme) { baseTheme = 'hc-black'; } else { baseTheme = this.stateService.getItem(THEME_STORAGE_KEY, 'vs-dark').split(' ')[0]; diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 05243e74ee8..5005fe95c8b 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -212,8 +212,8 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private registerListeners(): void { - // React to HC color scheme changes (Windows) - if (isWindows) { + // React to HC color scheme changes (Windows, macOS) + if (isWindows || isMacintosh) { nativeTheme.on('updated', () => { if (nativeTheme.shouldUseInvertedColorScheme || nativeTheme.shouldUseHighContrastColors) { this.sendToAll('vscode:enterHighContrast'); diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index f608c5ff52d..61102025606 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -258,9 +258,9 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; 'window.autoDetectHighContrast': { 'type': 'boolean', 'default': true, - 'description': nls.localize('autoDetectHighContrast', "If enabled, will automatically change to high contrast theme if Windows is using a high contrast theme, and to dark theme when switching away from a Windows high contrast theme."), + 'description': nls.localize('autoDetectHighContrast', "If enabled, will automatically change to high contrast theme if the OS is using a high contrast theme, and to dark theme when switching away from a high contrast theme."), 'scope': ConfigurationScope.APPLICATION, - 'included': isWindows + 'included': isWindows || isMacintosh }, 'window.doubleClickIconToClose': { 'type': 'boolean', diff --git a/src/vs/workbench/services/themes/common/themeConfiguration.ts b/src/vs/workbench/services/themes/common/themeConfiguration.ts index f8276cdc5cc..cd4ad6203f5 100644 --- a/src/vs/workbench/services/themes/common/themeConfiguration.ts +++ b/src/vs/workbench/services/themes/common/themeConfiguration.ts @@ -14,7 +14,7 @@ import { workbenchColorsSchemaId } from 'vs/platform/theme/common/colorRegistry' import { tokenStylingSchemaId } from 'vs/platform/theme/common/tokenClassificationRegistry'; import { ThemeSettings, IWorkbenchColorTheme, IWorkbenchFileIconTheme, IColorCustomizations, ITokenColorCustomizations, IWorkbenchProductIconTheme, ISemanticTokenColorCustomizations, IExperimentalSemanticTokenColorCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { isWeb } from 'vs/base/common/platform'; +import { isMacintosh, isWeb, isWindows } from 'vs/base/common/platform'; const DEFAULT_THEME_DARK_SETTING_VALUE = 'Default Dark+'; const DEFAULT_THEME_LIGHT_SETTING_VALUE = 'Default Light+'; @@ -60,6 +60,7 @@ const preferredHCThemeSettingSchema: IConfigurationPropertySchema = { default: DEFAULT_THEME_HC_SETTING_VALUE, enum: colorThemeSettingEnum, enumDescriptions: colorThemeSettingEnumDescriptions, + included: isWindows || isMacintosh, errorMessage: nls.localize('colorThemeError', "Theme is unknown or not installed."), }; const detectColorSchemeSettingSchema: IConfigurationPropertySchema = { From 0eb11e8f0cb4c98019921c7aeb1c837c74d23f40 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 27 Aug 2020 10:28:25 +0200 Subject: [PATCH 587/736] align names, https://github.com/microsoft/vscode/issues/105283 --- src/vs/vscode.proposed.d.ts | 4 ++-- src/vs/workbench/api/common/extHostNotebook.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 7358ec83ed8..6dbc01f5a34 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1343,8 +1343,8 @@ declare module 'vscode' { export interface NotebookEditorCellEdit { - replaceCells(from: number, to: number, cells: NotebookCellData[]): void; - replaceOutputs(index: number, outputs: CellOutput[]): void; + replaceCells(start: number, end: number, cells: NotebookCellData[]): void; + replaceOutput(index: number, outputs: CellOutput[]): void; replaceMetadata(index: number, metadata: NotebookCellMetadata): void; /** @deprecated */ diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 8a301cc9168..7bb9f60586a 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -527,7 +527,7 @@ export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellE }); } - replaceOutputs(index: number, outputs: vscode.CellOutput[]): void { + replaceOutput(index: number, outputs: vscode.CellOutput[]): void { this._throwIfFinalized(); this._collectedEdits.push({ editType: CellEditType.Output, From 0de8d51904589cba1e8deb764fb4d74faad01f5f Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 27 Aug 2020 11:07:20 +0200 Subject: [PATCH 588/736] Finalize task detail API Fixes #69785 --- extensions/npm/src/npmView.ts | 2 +- extensions/npm/src/tasks.ts | 2 +- .../typescript-language-features/src/task/taskProvider.ts | 2 +- .../src/singlefolder-tests/workspace.tasks.test.ts | 6 +++--- src/vs/vscode.d.ts | 5 +++++ src/vs/vscode.proposed.d.ts | 7 ------- src/vs/workbench/api/common/extHost.api.impl.ts | 1 - src/vs/workbench/api/common/extHostTask.ts | 4 ++-- src/vs/workbench/api/common/extHostTypes.ts | 2 +- src/vs/workbench/api/node/extHostTask.ts | 2 +- 10 files changed, 15 insertions(+), 18 deletions(-) diff --git a/extensions/npm/src/npmView.ts b/extensions/npm/src/npmView.ts index 72ec421f775..c7c7835fa04 100644 --- a/extensions/npm/src/npmView.ts +++ b/extensions/npm/src/npmView.ts @@ -7,7 +7,7 @@ import { JSONVisitor, visit } from 'jsonc-parser'; import * as path from 'path'; import { commands, Event, EventEmitter, ExtensionContext, - Selection, Task2 as Task, + Selection, Task, TaskGroup, tasks, TextDocument, ThemeIcon, TreeDataProvider, TreeItem, TreeItemCollapsibleState, Uri, window, workspace, WorkspaceFolder } from 'vscode'; diff --git a/extensions/npm/src/tasks.ts b/extensions/npm/src/tasks.ts index 040e1f820cc..f7def2f8876 100644 --- a/extensions/npm/src/tasks.ts +++ b/extensions/npm/src/tasks.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { - TaskDefinition, Task2 as Task, TaskGroup, WorkspaceFolder, RelativePattern, ShellExecution, Uri, workspace, + TaskDefinition, Task, TaskGroup, WorkspaceFolder, RelativePattern, ShellExecution, Uri, workspace, DebugConfiguration, debug, TaskProvider, TextDocument, tasks, TaskScope, QuickPickItem } from 'vscode'; import * as path from 'path'; diff --git a/extensions/typescript-language-features/src/task/taskProvider.ts b/extensions/typescript-language-features/src/task/taskProvider.ts index 0024a3596f3..85d3b574ae3 100644 --- a/extensions/typescript-language-features/src/task/taskProvider.ts +++ b/extensions/typescript-language-features/src/task/taskProvider.ts @@ -203,7 +203,7 @@ class TscTaskProvider implements vscode.TaskProvider { } private getBuildTask(workspaceFolder: vscode.WorkspaceFolder | undefined, label: string, command: string, args: string[], buildTaskidentifier: TypeScriptTaskDefinition): vscode.Task { - const buildTask = new vscode.Task2( + const buildTask = new vscode.Task( buildTaskidentifier, workspaceFolder || vscode.TaskScope.Workspace, localize('buildTscLabel', 'build - {0}', label), diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.tasks.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.tasks.test.ts index 53acdcf23dc..37426834335 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.tasks.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.tasks.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { window, tasks, Disposable, TaskDefinition, Task, EventEmitter, CustomExecution, Pseudoterminal, TaskScope, commands, Task2, env, UIKind, ShellExecution, TaskExecution, Terminal, Event } from 'vscode'; +import { window, tasks, Disposable, TaskDefinition, Task, EventEmitter, CustomExecution, Pseudoterminal, TaskScope, commands, env, UIKind, ShellExecution, TaskExecution, Terminal, Event } from 'vscode'; // Disable tasks tests: // - Web https://github.com/microsoft/vscode/issues/90528 @@ -94,7 +94,7 @@ import { window, tasks, Disposable, TaskDefinition, Task, EventEmitter, CustomEx }; return Promise.resolve(pty); }); - const task = new Task2(kind, TaskScope.Workspace, taskName, taskType, execution); + const task = new Task(kind, TaskScope.Workspace, taskName, taskType, execution); result.push(task); return result; }, @@ -151,7 +151,7 @@ import { window, tasks, Disposable, TaskDefinition, Task, EventEmitter, CustomEx }; return Promise.resolve(pty); }); - const task = new Task2(kind, TaskScope.Workspace, taskName, taskType, execution); + const task = new Task(kind, TaskScope.Workspace, taskName, taskType, execution); result.push(task); return result; }, diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index ffa13e24bfc..9a09ec73ed2 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -6205,6 +6205,11 @@ declare module 'vscode' { */ name: string; + /** + * A detail to show for the task on a second line in places where the task's name is displayed. + */ + detail?: string; + /** * The task's execution engine */ diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 6dbc01f5a34..c898362a2e4 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -963,13 +963,6 @@ declare module 'vscode' { } //#endregion - /** - * A task to execute - */ - export class Task2 extends Task { - detail?: string; - } - //#region Task presentation group: https://github.com/microsoft/vscode/issues/47265 export interface TaskPresentationOptions { /** diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 2f4ce5afda9..27f1e8b74ce 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1098,7 +1098,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I SymbolKind: extHostTypes.SymbolKind, SymbolTag: extHostTypes.SymbolTag, Task: extHostTypes.Task, - Task2: extHostTypes.Task, TaskGroup: extHostTypes.TaskGroup, TaskPanelKind: extHostTypes.TaskPanelKind, TaskRevealKind: extHostTypes.TaskRevealKind, diff --git a/src/vs/workbench/api/common/extHostTask.ts b/src/vs/workbench/api/common/extHostTask.ts index cb118977cbd..3fb57723234 100644 --- a/src/vs/workbench/api/common/extHostTask.ts +++ b/src/vs/workbench/api/common/extHostTask.ts @@ -269,8 +269,8 @@ export namespace TaskDTO { presentationOptions: TaskPresentationOptionsDTO.from(value.presentationOptions), problemMatchers: value.problemMatchers, hasDefinedMatchers: (value as types.Task).hasDefinedMatchers, - runOptions: (value).runOptions ? (value).runOptions : { reevaluateOnRerun: true }, - detail: (value).detail + runOptions: value.runOptions ? value.runOptions : { reevaluateOnRerun: true }, + detail: value.detail }; return result; } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 905debce535..3cc938b9889 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1875,7 +1875,7 @@ export class CustomExecution implements vscode.CustomExecution { } @es5ClassCompat -export class Task implements vscode.Task2 { +export class Task implements vscode.Task { private static ExtensionCallbackType: string = 'customExecution'; private static ProcessType: string = 'process'; diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index 0715f6affdb..01fc86aeccf 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -99,7 +99,7 @@ export class ExtHostTask extends ExtHostTaskBase { // The ID is calculated on the main thread task side, so, let's call into it here. // We need the task id's pre-computed for custom task executions because when OnDidStartTask // is invoked, we have to be able to map it back to our data. - taskIdPromises.push(this.addCustomExecution(taskDTO, task, true)); + taskIdPromises.push(this.addCustomExecution(taskDTO, task, true)); } } } From fdf5b95a51c540d17d55c90a8ed3d9f7f9022279 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 27 Aug 2020 11:24:55 +0200 Subject: [PATCH 589/736] replace ICellInsertEdit, ICellDeleteEdit with ICellReplaceEdit, https://github.com/microsoft/vscode/issues/105283 --- .../api/browser/mainThreadNotebook.ts | 3 +- .../workbench/api/common/extHostNotebook.ts | 48 +++++----------- src/vs/workbench/api/common/extHostTypes.ts | 3 +- .../notebook/browser/notebookServiceImpl.ts | 2 +- .../common/model/notebookTextModel.ts | 55 +++++++++++++++---- .../contrib/notebook/common/notebookCommon.ts | 20 +++---- .../notebook/test/notebookTextModel.test.ts | 44 +++++++++++---- 7 files changed, 103 insertions(+), 72 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index cda01b3eab6..91d4c528fde 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -392,8 +392,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo mainthreadTextModel.transientOptions = options; const edits: ICellEditOperation[] = [ - { editType: CellEditType.Delete, count: mainthreadTextModel.cells.length, index: 0 }, - { editType: CellEditType.Insert, index: 0, cells: data.cells } + { editType: CellEditType.Replace, index: 0, count: mainthreadTextModel.cells.length, cells: data.cells } ]; this._notebookService.transformEditsOutputs(mainthreadTextModel, edits); diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 7bb9f60586a..2c6410400de 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -22,7 +22,7 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; -import { addIdToOutput, CellEditType, CellOutputKind, CellStatusbarAlignment, CellUri, diff, ICellDeleteEdit, ICellDto2, ICellEditOperation, ICellInsertEdit, IMainCellDto, INotebookCellStatusBarEntry, INotebookDisplayOrder, INotebookEditData, INotebookKernelInfoDto2, IProcessedOutput, NotebookCellMetadata, NotebookCellsChangedEvent, NotebookCellsChangeType, NotebookCellsSplice2, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { addIdToOutput, CellEditType, CellOutputKind, CellStatusbarAlignment, CellUri, diff, ICellEditOperation, ICellReplaceEdit, IMainCellDto, INotebookCellStatusBarEntry, INotebookDisplayOrder, INotebookEditData, INotebookKernelInfoDto2, IProcessedOutput, NotebookCellMetadata, NotebookCellsChangedEvent, NotebookCellsChangeType, NotebookCellsSplice2, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import * as vscode from 'vscode'; import { Cache } from './cache'; import { ResourceMap } from 'vs/base/common/map'; @@ -539,29 +539,17 @@ export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellE replaceCells(from: number, to: number, cells: vscode.NotebookCellData[]): void { this._throwIfFinalized(); - // deletion - if (to > from) { - this._collectedEdits.push({ - editType: CellEditType.Delete, - index: from, - count: to - from - }); - } - // insert - if (cells.length > 0) { - this._collectedEdits.push({ - editType: CellEditType.Insert, - index: from, - cells: cells.map(data => { - return { - cellKind: data.cellKind, - language: data.language, - outputs: data.outputs.map(output => addIdToOutput(output)), - source: data.source - }; - }) - }); - } + this._collectedEdits.push({ + editType: CellEditType.Replace, + index: from, + count: to - from, + cells: cells.map(data => { + return { + ...data, + outputs: data.outputs.map(output => addIdToOutput(output)), + }; + }) + }); } insert(index: number, content: string | string[], language: string, type: CellKind, outputs: vscode.CellOutput[], metadata: vscode.NotebookCellMetadata | undefined): void { @@ -716,16 +704,10 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook const prevIndex = compressedEditsIndex; const prev = compressedEdits[prevIndex]; - if (prev.editType === CellEditType.Insert && editData.edits[i].editType === CellEditType.Insert) { + if (prev.editType === CellEditType.Replace && editData.edits[i].editType === CellEditType.Replace) { if (prev.index === editData.edits[i].index) { - prev.cells.push(...(editData.edits[i] as ICellInsertEdit).cells); - continue; - } - } - - if (prev.editType === CellEditType.Delete && editData.edits[i].editType === CellEditType.Delete) { - if (prev.index === editData.edits[i].index) { - prev.count += (editData.edits[i] as ICellDeleteEdit).count; + prev.cells.push(...(editData.edits[i] as ICellReplaceEdit).cells); + prev.count += (editData.edits[i] as ICellReplaceEdit).count; continue; } } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 3cc938b9889..bb8a0e560d3 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -632,8 +632,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { // --- cell replaceCells(uri: URI, start: number, end: number, cells: vscode.NotebookCellData[], metadata?: vscode.WorkspaceEditEntryMetadata): void { - this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.Delete, index: start, count: end - start } }); - this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.Insert, index: start, cells: cells.map(cell => ({ ...cell, outputs: cell.outputs.map(output => addIdToOutput(output)) })) } }); + this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.Replace, index: start, count: end - start, cells: cells.map(cell => ({ ...cell, outputs: cell.outputs.map(output => addIdToOutput(output)) })) } }); } replaceCellOutput(uri: URI, index: number, outputs: vscode.CellOutput[], metadata?: vscode.WorkspaceEditEntryMetadata): void { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 5a72f03823c..80598b122bb 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -720,7 +720,7 @@ export class NotebookService extends Disposable implements INotebookService, ICu transformEditsOutputs(textModel: NotebookTextModel, edits: ICellEditOperation[]) { edits.forEach((edit) => { - if (edit.editType === CellEditType.Insert) { + if (edit.editType === CellEditType.Replace) { edit.cells.forEach((cell) => { const outputs = cell.outputs; outputs.map((output) => { diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index a27d7e8d218..1f68435cbdc 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -232,7 +232,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel const edits = rawEdits.map((edit, index) => { return { edit, - end: edit.editType === CellEditType.Delete ? edit.index + edit.count : edit.index, + end: edit.editType === CellEditType.Replace ? edit.index + edit.count : edit.index, originalIndex: index, }; }).sort((a, b) => { @@ -241,16 +241,8 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel for (const { edit } of edits) { switch (edit.editType) { - case CellEditType.Insert: - const mainCells = edit.cells.map(cell => { - const cellHandle = this._cellhandlePool++; - const cellUri = CellUri.generate(this.uri, cellHandle); - return new NotebookCellTextModel(cellUri, cellHandle, cell.source, cell.language, cell.cellKind, cell.outputs || [], cell.metadata, this.transientOptions, this._modelService); - }); - this.insertNewCell(edit.index, mainCells, false); - break; - case CellEditType.Delete: - this.removeCell(edit.index, edit.count, false); + case CellEditType.Replace: + this._replaceCells(edit.index, edit.count, edit.cells); break; case CellEditType.Output: //TODO@joh,@rebornix no event, no undo stop (?) @@ -302,6 +294,47 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel return true; } + private _replaceCells(index: number, count: number, cellDtos: ICellDto2[]): void { + + if (count === 0 && cellDtos.length === 0) { + return; + } + + this._isUntitled = false; //TODO@rebornix fishy? + + // prepare remove + for (let i = index; i < index + count; i++) { + const cell = this.cells[i]; + this._cellListeners.get(cell.handle)?.dispose(); + this._cellListeners.delete(cell.handle); + } + + // prepare add + const cells = cellDtos.map(cellDto => { + const cellHandle = this._cellhandlePool++; + const cellUri = CellUri.generate(this.uri, cellHandle); + const cell = new NotebookCellTextModel( + cellUri, cellHandle, + cellDto.source, cellDto.language, cellDto.cellKind, cellDto.outputs || [], cellDto.metadata, this.transientOptions, + this._modelService + ); + const dirtyStateListener = cell.onDidChangeContent(() => { + this.setDirty(true); + this._increaseVersionId(); + this._onDidChangeContent.fire(); + }); + this._cellListeners.set(cell.handle, dirtyStateListener); + this._mapping.set(cell.handle, cell); + return cell; + }); + + // make change + this.cells.splice(index, count, ...cells); + this.setDirty(true); + this._increaseVersionId(); + this._onDidChangeContent.fire(); + } + handleEdit(label: string | undefined, undo: () => void, redo: () => void): void { this._operationManager.pushEditOperation({ type: UndoRedoElementType.Resource, diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index fcb70b1599c..c4e05bd2be4 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -416,10 +416,9 @@ export interface NotebookCellsChangeMetadataEvent { export type NotebookCellsChangedEvent = NotebookCellsInitializeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookCellClearOutputEvent | NotebookCellsClearOutputEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent; export const enum CellEditType { - Insert = 1, - Delete = 2, - Output = 3, - Metadata = 4, + Replace = 1, + Output = 2, + Metadata = 3, } export interface ICellDto2 { @@ -430,16 +429,11 @@ export interface ICellDto2 { metadata?: NotebookCellMetadata; } -export interface ICellInsertEdit { - editType: CellEditType.Insert; - index: number; - cells: ICellDto2[]; -} - -export interface ICellDeleteEdit { - editType: CellEditType.Delete; +export interface ICellReplaceEdit { + editType: CellEditType.Replace; index: number; count: number; + cells: ICellDto2[]; } export interface ICellOutputEdit { @@ -454,7 +448,7 @@ export interface ICellMetadataEdit { metadata: NotebookCellMetadata; } -export type ICellEditOperation = ICellInsertEdit | ICellDeleteEdit | ICellOutputEdit | ICellMetadataEdit; +export type ICellEditOperation = ICellReplaceEdit | ICellOutputEdit | ICellMetadataEdit; export interface INotebookEditData { documentVersionId: number; diff --git a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts index 444121d2af7..010ddba6af2 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts @@ -30,8 +30,8 @@ suite('NotebookTextModel', () => { ], (editor, viewModel, textModel) => { textModel.applyEdit(textModel.versionId, [ - { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], textModelService)] }, - { editType: CellEditType.Insert, index: 3, cells: [new TestCell(viewModel.viewType, 6, 'var f = 6;', 'javascript', CellKind.Code, [], textModelService)] }, + { editType: CellEditType.Replace, index: 1, count: 0, cells: [new TestCell(viewModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], textModelService)] }, + { editType: CellEditType.Replace, index: 3, count: 0, cells: [new TestCell(viewModel.viewType, 6, 'var f = 6;', 'javascript', CellKind.Code, [], textModelService)] }, ], true); assert.equal(textModel.cells.length, 6); @@ -55,8 +55,8 @@ suite('NotebookTextModel', () => { ], (editor, viewModel, textModel) => { textModel.applyEdit(textModel.versionId, [ - { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], textModelService)] }, - { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 6, 'var f = 6;', 'javascript', CellKind.Code, [], textModelService)] }, + { editType: CellEditType.Replace, index: 1, count: 0, cells: [new TestCell(viewModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], textModelService)] }, + { editType: CellEditType.Replace, index: 1, count: 0, cells: [new TestCell(viewModel.viewType, 6, 'var f = 6;', 'javascript', CellKind.Code, [], textModelService)] }, ], true); assert.equal(textModel.cells.length, 6); @@ -80,8 +80,8 @@ suite('NotebookTextModel', () => { ], (editor, viewModel, textModel) => { textModel.applyEdit(textModel.versionId, [ - { editType: CellEditType.Delete, index: 1, count: 1 }, - { editType: CellEditType.Delete, index: 3, count: 1 }, + { editType: CellEditType.Replace, index: 1, count: 1, cells: [] }, + { editType: CellEditType.Replace, index: 3, count: 1, cells: [] }, ], true); assert.equal(textModel.cells[0].getValue(), 'var a = 1;'); @@ -103,8 +103,8 @@ suite('NotebookTextModel', () => { ], (editor, viewModel, textModel) => { textModel.applyEdit(textModel.versionId, [ - { editType: CellEditType.Delete, index: 1, count: 1 }, - { editType: CellEditType.Insert, index: 3, cells: [new TestCell(viewModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], textModelService)] }, + { editType: CellEditType.Replace, index: 1, count: 1, cells: [] }, + { editType: CellEditType.Replace, index: 3, count: 0, cells: [new TestCell(viewModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], textModelService)] }, ], true); assert.equal(textModel.cells.length, 4); @@ -128,8 +128,32 @@ suite('NotebookTextModel', () => { ], (editor, viewModel, textModel) => { textModel.applyEdit(textModel.versionId, [ - { editType: CellEditType.Delete, index: 1, count: 1 }, - { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], textModelService)] }, + { editType: CellEditType.Replace, index: 1, count: 1, cells: [] }, + { editType: CellEditType.Replace, index: 1, count: 0, cells: [new TestCell(viewModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], textModelService)] }, + ], true); + + assert.equal(textModel.cells.length, 4); + assert.equal(textModel.cells[0].getValue(), 'var a = 1;'); + assert.equal(textModel.cells[1].getValue(), 'var e = 5;'); + assert.equal(textModel.cells[2].getValue(), 'var c = 3;'); + } + ); + }); + + test('(replace) delete + insert at same position', function () { + withTestNotebook( + instantiationService, + blukEditService, + undoRedoService, + [ + ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], + ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }], + ['var c = 3;', 'javascript', CellKind.Code, [], { editable: false }], + ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] + ], + (editor, viewModel, textModel) => { + textModel.applyEdit(textModel.versionId, [ + { editType: CellEditType.Replace, index: 1, count: 1, cells: [new TestCell(viewModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], textModelService)] }, ], true); assert.equal(textModel.cells.length, 4); From 470966b066f88e86629451b57b004b638816ccba Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 27 Aug 2020 11:58:01 +0200 Subject: [PATCH 590/736] add model version to notebook edits when possible, https://github.com/microsoft/vscode/issues/105283 --- src/vs/workbench/api/common/extHost.api.impl.ts | 4 ++-- src/vs/workbench/api/common/extHostNotebook.ts | 12 ++++++++---- src/vs/workbench/api/common/extHostTextEditors.ts | 12 ++++++------ src/vs/workbench/api/common/extHostTypeConverters.ts | 5 +++-- .../test/browser/api/extHostTextEditors.test.ts | 2 +- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 27f1e8b74ce..1a7edca9370 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -128,7 +128,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostDocuments = rpcProtocol.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors)); const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors, extHostLogService)); const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostLogService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadTextEditors))); - const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors)); + const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, initData.environment, extHostLogService, extensionStoragePaths)); + const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors, extHostNotebook)); const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService)); const extHostEditorInsets = rpcProtocol.set(ExtHostContext.ExtHostEditorInsets, new ExtHostEditorInsets(rpcProtocol.getProxy(MainContext.MainThreadEditorInsets), extHostEditors, initData.environment)); const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol, extHostLogService)); @@ -140,7 +141,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostComment = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostCommands, extHostDocuments)); const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress))); const extHostLabelService = rpcProtocol.set(ExtHostContext.ExtHosLabelService, new ExtHostLabelService(rpcProtocol)); - const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, initData.environment, extHostLogService, extensionStoragePaths)); const extHostTheming = rpcProtocol.set(ExtHostContext.ExtHostTheming, new ExtHostTheming(rpcProtocol)); const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol)); const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol, extHostCommands)); diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 2c6410400de..17db2365cf1 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -893,10 +893,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN return this._activeNotebookEditor; } - get notebookDocuments() { - return [...this._documents.values()]; - } - private _onDidOpenNotebookDocument = new Emitter(); onDidOpenNotebookDocument: Event = this._onDidOpenNotebookDocument.event; private _onDidCloseNotebookDocument = new Emitter(); @@ -941,6 +937,14 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN }); } + get notebookDocuments() { + return [...this._documents.values()]; + } + + lookupNotebookDocument(uri: URI): ExtHostNotebookDocument | undefined { + return this._documents.get(uri); + } + registerNotebookContentProvider( extension: IExtensionDescription, viewType: string, diff --git a/src/vs/workbench/api/common/extHostTextEditors.ts b/src/vs/workbench/api/common/extHostTextEditors.ts index e99f513e940..a2ba800bf5c 100644 --- a/src/vs/workbench/api/common/extHostTextEditors.ts +++ b/src/vs/workbench/api/common/extHostTextEditors.ts @@ -7,6 +7,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import * as arrays from 'vs/base/common/arrays'; import { ExtHostEditorsShape, IEditorPropertiesChangeData, IMainContext, ITextDocumentShowOptions, ITextEditorPositionData, MainContext, MainThreadTextEditorsShape } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; +import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; import { ExtHostTextEditor, TextEditorDecorationType } from 'vs/workbench/api/common/extHostTextEditor'; import * as TypeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { TextEditorSelectionChangeKind } from 'vs/workbench/api/common/extHostTypes'; @@ -28,16 +29,15 @@ export class ExtHostEditors implements ExtHostEditorsShape { readonly onDidChangeActiveTextEditor: Event = this._onDidChangeActiveTextEditor.event; readonly onDidChangeVisibleTextEditors: Event = this._onDidChangeVisibleTextEditors.event; - - private _proxy: MainThreadTextEditorsShape; - private _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors; + private readonly _proxy: MainThreadTextEditorsShape; constructor( mainContext: IMainContext, - extHostDocumentsAndEditors: ExtHostDocumentsAndEditors, + private readonly _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors, + private readonly _extHostNotebooks: ExtHostNotebookController, ) { this._proxy = mainContext.getProxy(MainContext.MainThreadTextEditors); - this._extHostDocumentsAndEditors = extHostDocumentsAndEditors; + this._extHostDocumentsAndEditors.onDidChangeVisibleTextEditors(e => this._onDidChangeVisibleTextEditors.fire(e)); this._extHostDocumentsAndEditors.onDidChangeActiveTextEditor(e => this._onDidChangeActiveTextEditor.fire(e)); @@ -93,7 +93,7 @@ export class ExtHostEditors implements ExtHostEditorsShape { } applyWorkspaceEdit(edit: vscode.WorkspaceEdit): Promise { - const dto = TypeConverters.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors); + const dto = TypeConverters.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors, this._extHostNotebooks); return this._proxy.$tryApplyWorkspaceEdit(dto); } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index b09d7952069..b27490d45a1 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -31,6 +31,7 @@ import { LogLevel as _MainLogLevel } from 'vs/platform/log/common/log'; import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays'; import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions'; import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands'; +import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; export interface PositionLike { line: number; @@ -505,7 +506,7 @@ export namespace TextEdit { } export namespace WorkspaceEdit { - export function from(value: vscode.WorkspaceEdit, documents?: ExtHostDocumentsAndEditors): extHostProtocol.IWorkspaceEditDto { + export function from(value: vscode.WorkspaceEdit, documents?: ExtHostDocumentsAndEditors, notebooks?: ExtHostNotebookController): extHostProtocol.IWorkspaceEditDto { const result: extHostProtocol.IWorkspaceEditDto = { edits: [] }; @@ -539,7 +540,7 @@ export namespace WorkspaceEdit { resource: entry.uri, edit: entry.edit, metadata: entry.metadata, - modelVersionId: undefined, // todo@jrieken + modelVersionId: notebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version }); } } diff --git a/src/vs/workbench/test/browser/api/extHostTextEditors.test.ts b/src/vs/workbench/test/browser/api/extHostTextEditors.test.ts index 0d94ec01908..a1778a00358 100644 --- a/src/vs/workbench/test/browser/api/extHostTextEditors.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTextEditors.test.ts @@ -40,7 +40,7 @@ suite('ExtHostTextEditors.applyWorkspaceEdit', () => { EOL: '\n', }] }); - editors = new ExtHostEditors(rpcProtocol, documentsAndEditors); + editors = new ExtHostEditors(rpcProtocol, documentsAndEditors, null!); }); test('uses version id if document available', async () => { From dfa931d786b861fab10379ec13db997ee6116cc6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 27 Aug 2020 13:33:45 +0200 Subject: [PATCH 591/736] more Dto tweaks --- src/vs/base/common/types.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/vs/base/common/types.ts b/src/vs/base/common/types.ts index 3a34cb7a1c6..eef27600bd7 100644 --- a/src/vs/base/common/types.ts +++ b/src/vs/base/common/types.ts @@ -258,14 +258,12 @@ export type UriDto = { [K in keyof T]: T[K] extends URI /** * Mapped-type that replaces all occurrences of URI with UriComponents and * drops all functions. - * todo@joh use toJSON-results */ -export type Dto = { [K in keyof T]: T[K] extends { toJSON(): infer U } +export type Dto = T extends { toJSON(): infer U } ? U - : T[K] extends Function - ? never - : Dto }; - + : T extends object + ? { [k in keyof T]: Dto; } + : T; export function NotImplementedProxy(name: string): { new(): T } { return class { From e5be134f6243154e7eb72121d7b20e84cc58aefb Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 27 Aug 2020 13:59:42 +0200 Subject: [PATCH 592/736] Show task detail on already selected default Fixes #105315 --- .../workbench/contrib/tasks/browser/abstractTaskService.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 4daf3cbe2ae..883e2ffcb85 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -3059,7 +3059,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (selectedTask) { selectedEntry = { label: nls.localize('TaskService.defaultBuildTaskExists', '{0} is already marked as the default build task', selectedTask.getQualifiedLabel()), - task: selectedTask + task: selectedTask, + detail: this.showDetail() ? selectedTask.configurationProperties.detail : undefined }; } this.showIgnoredFoldersMessage().then(() => { @@ -3110,7 +3111,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (selectedTask) { selectedEntry = { label: nls.localize('TaskService.defaultTestTaskExists', '{0} is already marked as the default test task.', selectedTask.getQualifiedLabel()), - task: selectedTask + task: selectedTask, + detail: this.showDetail() ? selectedTask.configurationProperties.detail : undefined }; } From 8743d95c76e76b5b665c5b1b1ba2ef9ebdbbad49 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 27 Aug 2020 09:55:35 +0200 Subject: [PATCH 593/736] introduce canInstall api in extension management service --- .../common/extensionGalleryService.ts | 11 +++++-- .../common/extensionManagement.ts | 2 ++ .../common/extensionManagementIpc.ts | 5 ++++ .../node/extensionManagementService.ts | 4 +++ .../extensionsActions.test.ts | 2 +- .../electron-browser/extensionsViews.test.ts | 3 +- .../sandbox.simpleservices.ts | 1 + .../extensionManagementServerService.ts | 4 +-- .../common/extensionManagementService.ts | 9 ++++++ .../remoteExtensionManagementService.ts | 30 +++++++++++++++++++ .../common/webExtensionManagementService.ts | 8 +++++ .../extensionManagementServerService.ts | 14 +++------ .../remoteExtensionManagementService.ts} | 26 ++++++++-------- 13 files changed, 90 insertions(+), 29 deletions(-) create mode 100644 src/vs/workbench/services/extensionManagement/common/remoteExtensionManagementService.ts rename src/vs/workbench/services/{extensions/electron-browser/remoteExtensionManagementIpc.ts => extensionManagement/electron-browser/remoteExtensionManagementService.ts} (86%) diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index 52949788147..513ce8e92a7 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -115,7 +115,8 @@ const PropertyType = { Dependency: 'Microsoft.VisualStudio.Code.ExtensionDependencies', ExtensionPack: 'Microsoft.VisualStudio.Code.ExtensionPack', Engine: 'Microsoft.VisualStudio.Code.Engine', - LocalizedLanguages: 'Microsoft.VisualStudio.Code.LocalizedLanguages' + LocalizedLanguages: 'Microsoft.VisualStudio.Code.LocalizedLanguages', + WebExtension: 'Microsoft.VisualStudio.Code.WebExtension' }; interface ICriterium { @@ -266,6 +267,11 @@ function getIsPreview(flags: string): boolean { return flags.indexOf('preview') !== -1; } +function getIsWebExtension(version: IRawGalleryExtensionVersion): boolean { + const webExtensionProperty = version.properties ? version.properties.find(p => p.key === PropertyType.WebExtension) : undefined; + return !!webExtensionProperty && webExtensionProperty.value === 'true'; +} + function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGalleryExtensionVersion, index: number, query: Query, querySource?: string): IGalleryExtension { const assets = { manifest: getVersionAsset(version, AssetType.Manifest), @@ -301,7 +307,8 @@ function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGaller dependencies: getExtensions(version, PropertyType.Dependency), extensionPack: getExtensions(version, PropertyType.ExtensionPack), engine: getEngine(version), - localizedLanguages: getLocalizedLanguages(version) + localizedLanguages: getLocalizedLanguages(version), + webExtension: getIsWebExtension(version) }, /* __GDPR__FRAGMENT__ "GalleryExtensionTelemetryData2" : { diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index a572f0a2860..609e3a501e2 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -19,6 +19,7 @@ export interface IGalleryExtensionProperties { extensionPack?: string[]; engine?: string; localizedLanguages?: string[]; + webExtension?: boolean; } export interface IGalleryExtensionAsset { @@ -204,6 +205,7 @@ export interface IExtensionManagementService { unzip(zipLocation: URI): Promise; getManifest(vsix: URI): Promise; install(vsix: URI, isMachineScoped?: boolean): Promise; + canInstall(extension: IGalleryExtension): Promise; installFromGallery(extension: IGalleryExtension, isMachineScoped?: boolean): Promise; uninstall(extension: ILocalExtension, force?: boolean): Promise; reinstallFromGallery(extension: ILocalExtension): Promise; diff --git a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts index d497780449a..048aa90adf3 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts @@ -63,6 +63,7 @@ export class ExtensionManagementChannel implements IServerChannel { case 'unzip': return this.service.unzip(transformIncomingURI(args[0], uriTransformer)); case 'install': return this.service.install(transformIncomingURI(args[0], uriTransformer)); case 'getManifest': return this.service.getManifest(transformIncomingURI(args[0], uriTransformer)); + case 'canInstall': return this.service.canInstall(args[0]); case 'installFromGallery': return this.service.installFromGallery(args[0]); case 'uninstall': return this.service.uninstall(transformIncomingExtension(args[0], uriTransformer), args[1]); case 'reinstallFromGallery': return this.service.reinstallFromGallery(transformIncomingExtension(args[0], uriTransformer)); @@ -104,6 +105,10 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer return Promise.resolve(this.channel.call('getManifest', [vsix])); } + async canInstall(extension: IGalleryExtension): Promise { + return true; + } + installFromGallery(extension: IGalleryExtension): Promise { return Promise.resolve(this.channel.call('installFromGallery', [extension])).then(local => transformIncomingExtension(local, null)); } diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 7a760a66ca5..badeef4aa5d 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -238,6 +238,10 @@ export class ExtensionManagementService extends Disposable implements IExtension )); } + async canInstall(extension: IGalleryExtension): Promise { + return true; + } + async installFromGallery(extension: IGalleryExtension, isMachineScoped?: boolean): Promise { if (!this.galleryService.isEnabled()) { return Promise.reject(new Error(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled"))); diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts index 7c83b8ea7b1..8f260d90da0 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts @@ -101,7 +101,7 @@ async function setupTest() { instantiationService.stub(IExtensionManagementServerService, new class extends ExtensionManagementServerService { #localExtensionManagementServer: IExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', id: 'vscode-local' }; constructor() { - super(instantiationService.get(ISharedProcessService), instantiationService.get(IRemoteAgentService), instantiationService.get(IExtensionGalleryService), instantiationService.get(IConfigurationService), instantiationService.get(IProductService), instantiationService.get(ILogService), instantiationService.get(ILabelService)); + super(instantiationService.get(ISharedProcessService), instantiationService, instantiationService.get(IRemoteAgentService), instantiationService.get(ILabelService)); } get localExtensionManagementServer(): IExtensionManagementServer { return this.#localExtensionManagementServer; } set localExtensionManagementServer(server: IExtensionManagementServer) { } diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts index cbbaddea537..4bd61fe7b51 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts @@ -40,7 +40,6 @@ import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browse import { ExtensionIdentifier, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { ExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService'; -import { IProductService } from 'vs/platform/product/common/productService'; import { ILabelService } from 'vs/platform/label/common/label'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; @@ -101,7 +100,7 @@ suite('ExtensionsListView Tests', () => { instantiationService.stub(IExtensionManagementServerService, new class extends ExtensionManagementServerService { #localExtensionManagementServer: IExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', id: 'vscode-local' }; constructor() { - super(instantiationService.get(ISharedProcessService), instantiationService.get(IRemoteAgentService), instantiationService.get(IExtensionGalleryService), instantiationService.get(IConfigurationService), instantiationService.get(IProductService), instantiationService.get(ILogService), instantiationService.get(ILabelService)); + super(instantiationService.get(ISharedProcessService), instantiationService, instantiationService.get(IRemoteAgentService), instantiationService.get(ILabelService)); } get localExtensionManagementServer(): IExtensionManagementServer { return this.#localExtensionManagementServer; } set localExtensionManagementServer(server: IExtensionManagementServer) { } diff --git a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts index 61352e2ee83..596d7b2a8a9 100644 --- a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts +++ b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts @@ -840,6 +840,7 @@ class SimpleExtensionManagementService implements IExtensionManagementService { async unzip(zipLocation: URI): Promise { throw new Error('Method not implemented.'); } async getManifest(vsix: URI): Promise { throw new Error('Method not implemented.'); } async install(vsix: URI, isMachineScoped?: boolean): Promise { throw new Error('Method not implemented.'); } + async canInstall(extension: IGalleryExtension): Promise { throw new Error('Method not implemented.'); } async installFromGallery(extension: IGalleryExtension, isMachineScoped?: boolean): Promise { throw new Error('Method not implemented.'); } async uninstall(extension: ILocalExtension, force?: boolean): Promise { } async reinstallFromGallery(extension: ILocalExtension): Promise { } diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts index 388d6996081..dfee9ed2d2a 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts @@ -5,7 +5,6 @@ import { localize } from 'vs/nls'; import { IExtensionManagementServer, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; @@ -15,6 +14,7 @@ import { isWeb } from 'vs/base/common/platform'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { WebExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/webExtensionManagementService'; import { IExtension } from 'vs/platform/extensions/common/extensions'; +import { WebRemoteExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/remoteExtensionManagementService'; export class ExtensionManagementServerService implements IExtensionManagementServerService { @@ -31,7 +31,7 @@ export class ExtensionManagementServerService implements IExtensionManagementSer ) { const remoteAgentConnection = remoteAgentService.getConnection(); if (remoteAgentConnection) { - const extensionManagementService = new ExtensionManagementChannelClient(remoteAgentConnection!.getChannel('extensions')); + const extensionManagementService = instantiationService.createInstance(WebRemoteExtensionManagementService, remoteAgentConnection!.getChannel('extensions')); this.remoteExtensionManagementServer = { id: 'remote', extensionManagementService, diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts index 33eb56db3c2..a982b3ecc58 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts @@ -189,6 +189,15 @@ export class ExtensionManagementService extends Disposable implements IExtension return Promise.reject('No Servers'); } + async canInstall(gallery: IGalleryExtension): Promise { + for (const server of this.servers) { + if (await server.extensionManagementService.canInstall(gallery)) { + return true; + } + } + return false; + } + async installFromGallery(gallery: IGalleryExtension): Promise { // Only local server, install without any checks diff --git a/src/vs/workbench/services/extensionManagement/common/remoteExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/remoteExtensionManagementService.ts new file mode 100644 index 00000000000..908ce5a2ab1 --- /dev/null +++ b/src/vs/workbench/services/extensionManagement/common/remoteExtensionManagementService.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IExtensionManagementService, IGalleryExtension, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { canExecuteOnWorkspace } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; + +export class WebRemoteExtensionManagementService extends ExtensionManagementChannelClient implements IExtensionManagementService { + + constructor( + channel: IChannel, + @IExtensionGalleryService protected readonly galleryService: IExtensionGalleryService, + @IConfigurationService protected readonly configurationService: IConfigurationService, + @IProductService protected readonly productService: IProductService + ) { + super(channel); + } + + async canInstall(extension: IGalleryExtension): Promise { + const manifest = await this.galleryService.getManifest(extension, CancellationToken.None); + return !!manifest && canExecuteOnWorkspace(manifest, this.productService, this.configurationService); + } + +} diff --git a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts index 98a423df368..7282f156199 100644 --- a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts @@ -11,6 +11,7 @@ import { areSameExtensions } from 'vs/platform/extensionManagement/common/extens import { IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ILogService } from 'vs/platform/log/common/log'; import { Disposable } from 'vs/base/common/lifecycle'; +import { localize } from 'vs/nls'; export class WebExtensionManagementService extends Disposable implements IExtensionManagementService { @@ -40,7 +41,14 @@ export class WebExtensionManagementService extends Disposable implements IExtens return Promise.all(extensions.map(e => this.toLocalExtension(e))); } + async canInstall(gallery: IGalleryExtension): Promise { + return !!gallery.properties.webExtension; + } + async installFromGallery(gallery: IGalleryExtension): Promise { + if (!(await this.canInstall(gallery))) { + throw new Error(localize('non web extension', "Cannot install because {0} is not a web extension", gallery.displayName)); + } this.logService.info('Installing extension:', gallery.identifier.id); this._onInstallExtension.fire({ identifier: gallery.identifier, gallery }); try { diff --git a/src/vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService.ts b/src/vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService.ts index 0ac506857c7..fb573f0618f 100644 --- a/src/vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService.ts @@ -5,7 +5,6 @@ import { localize } from 'vs/nls'; import { Schemas } from 'vs/base/common/network'; -import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionManagementServer, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; @@ -13,12 +12,10 @@ import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { ILogService } from 'vs/platform/log/common/log'; -import { RemoteExtensionManagementChannelClient } from 'vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IProductService } from 'vs/platform/product/common/productService'; +import { DesktopRemoteExtensionManagementService } from 'vs/workbench/services/extensionManagement/electron-browser/remoteExtensionManagementService'; import { ILabelService } from 'vs/platform/label/common/label'; import { IExtension } from 'vs/platform/extensions/common/extensions'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; export class ExtensionManagementServerService implements IExtensionManagementServerService { @@ -31,11 +28,8 @@ export class ExtensionManagementServerService implements IExtensionManagementSer constructor( @ISharedProcessService sharedProcessService: ISharedProcessService, + @IInstantiationService instantiationService: IInstantiationService, @IRemoteAgentService remoteAgentService: IRemoteAgentService, - @IExtensionGalleryService galleryService: IExtensionGalleryService, - @IConfigurationService configurationService: IConfigurationService, - @IProductService productService: IProductService, - @ILogService logService: ILogService, @ILabelService labelService: ILabelService, ) { const localExtensionManagementService = new ExtensionManagementChannelClient(sharedProcessService.getChannel('extensions')); @@ -43,7 +37,7 @@ export class ExtensionManagementServerService implements IExtensionManagementSer this._localExtensionManagementServer = { extensionManagementService: localExtensionManagementService, id: 'local', label: localize('local', "Local") }; const remoteAgentConnection = remoteAgentService.getConnection(); if (remoteAgentConnection) { - const extensionManagementService = new RemoteExtensionManagementChannelClient(remoteAgentConnection.getChannel('extensions'), this.localExtensionManagementServer.extensionManagementService, galleryService, logService, configurationService, productService); + const extensionManagementService = instantiationService.createInstance(DesktopRemoteExtensionManagementService, remoteAgentConnection.getChannel('extensions'), this.localExtensionManagementServer); this.remoteExtensionManagementServer = { id: 'remote', extensionManagementService, diff --git a/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts b/src/vs/workbench/services/extensionManagement/electron-browser/remoteExtensionManagementService.ts similarity index 86% rename from src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts rename to src/vs/workbench/services/extensionManagement/electron-browser/remoteExtensionManagementService.ts index b11f3e7f7fa..749f01c3bc9 100644 --- a/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts +++ b/src/vs/workbench/services/extensionManagement/electron-browser/remoteExtensionManagementService.ts @@ -12,28 +12,30 @@ import { areSameExtensions } from 'vs/platform/extensionManagement/common/extens import { ILogService } from 'vs/platform/log/common/log'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { prefersExecuteOnUI } from 'vs/workbench/services/extensions/common/extensionsUtil'; -import { isNonEmptyArray, toArray } from 'vs/base/common/arrays'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { localize } from 'vs/nls'; import { IProductService } from 'vs/platform/product/common/productService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; import { generateUuid } from 'vs/base/common/uuid'; import { joinPath } from 'vs/base/common/resources'; +import { WebRemoteExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/remoteExtensionManagementService'; +import { IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -export class RemoteExtensionManagementChannelClient extends ExtensionManagementChannelClient { +export class DesktopRemoteExtensionManagementService extends WebRemoteExtensionManagementService implements IExtensionManagementService { - declare readonly _serviceBrand: undefined; + private readonly localExtensionManagementService: IExtensionManagementService; constructor( channel: IChannel, - private readonly localExtensionManagementService: IExtensionManagementService, - private readonly galleryService: IExtensionGalleryService, - private readonly logService: ILogService, - private readonly configurationService: IConfigurationService, - private readonly productService: IProductService + localExtensionManagementServer: IExtensionManagementServer, + @ILogService private readonly logService: ILogService, + @IExtensionGalleryService galleryService: IExtensionGalleryService, + @IConfigurationService configurationService: IConfigurationService, + @IProductService productService: IProductService ) { - super(channel); + super(channel, galleryService, configurationService, productService); + this.localExtensionManagementService = localExtensionManagementServer.extensionManagementService; } async install(vsix: URI): Promise { @@ -101,14 +103,14 @@ export class RemoteExtensionManagementChannelClient extends ExtensionManagementC const result = new Map(); const extensions = [...(manifest.extensionPack || []), ...(manifest.extensionDependencies || [])]; await this.getDependenciesAndPackedExtensionsRecursively(extensions, result, true, token); - return toArray(result.values()); + return [...result.values()]; } private async getAllWorkspaceDependenciesAndPackedExtensions(manifest: IExtensionManifest, token: CancellationToken): Promise { const result = new Map(); const extensions = [...(manifest.extensionPack || []), ...(manifest.extensionDependencies || [])]; await this.getDependenciesAndPackedExtensionsRecursively(extensions, result, false, token); - return toArray(result.values()); + return [...result.values()]; } private async getDependenciesAndPackedExtensionsRecursively(toGet: string[], result: Map, uiExtension: boolean, token: CancellationToken): Promise { From 4880a28f09026876873633eada50b1f6a4b22c09 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 27 Aug 2020 09:57:55 +0200 Subject: [PATCH 594/736] recommend extensions only when they are installable --- .../browser/configBasedRecommendations.ts | 7 ++- .../dynamicWorkspaceRecommendations.ts | 7 ++- .../browser/exeBasedRecommendations.ts | 6 ++- .../browser/experimentalRecommendations.ts | 6 ++- .../browser/extensionRecommendations.ts | 52 +++++++++++++------ .../extensionRecommendationsService.ts | 36 +++++++++---- .../extensions/browser/extensionsActions.ts | 18 ------- .../browser/fileBasedRecommendations.ts | 8 +-- .../browser/keymapRecommendations.ts | 6 ++- .../browser/workspaceRecommendations.ts | 8 +-- 10 files changed, 97 insertions(+), 57 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts index 10f9e97cad2..ca5d30091db 100644 --- a/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IExtensionTipsService, IConfigBasedExtensionTip } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionTipsService, IConfigBasedExtensionTip, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { localize } from 'vs/nls'; @@ -15,6 +15,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { IWorkspaceContextService, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; import { Emitter } from 'vs/base/common/event'; +import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; export class ConfigBasedRecommendations extends ExtensionRecommendations { @@ -36,6 +37,8 @@ export class ConfigBasedRecommendations extends ExtensionRecommendations { isExtensionAllowedToBeRecommended: (extensionId: string) => boolean, @IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, + @IExtensionManagementService extensionManagementService: IExtensionManagementService, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, @IInstantiationService instantiationService: IInstantiationService, @IConfigurationService configurationService: IConfigurationService, @INotificationService notificationService: INotificationService, @@ -43,7 +46,7 @@ export class ConfigBasedRecommendations extends ExtensionRecommendations { @IStorageService storageService: IStorageService, @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, ) { - super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, storageKeysSyncRegistryService); + super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, extensionsWorkbenchService, extensionManagementService, storageKeysSyncRegistryService); } protected async doActivate(): Promise { diff --git a/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts index 39694d6c0a7..17c8c513833 100644 --- a/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IFileService } from 'vs/platform/files/common/files'; @@ -18,6 +18,7 @@ import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionMa import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; type DynamicWorkspaceRecommendationsClassification = { count: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; @@ -44,9 +45,11 @@ export class DynamicWorkspaceRecommendations extends ExtensionRecommendations { @INotificationService notificationService: INotificationService, @ITelemetryService telemetryService: ITelemetryService, @IStorageService storageService: IStorageService, + @IExtensionManagementService extensionManagementService: IExtensionManagementService, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, ) { - super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, storageKeysSyncRegistryService); + super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, extensionsWorkbenchService, extensionManagementService, storageKeysSyncRegistryService); } protected async doActivate(): Promise { diff --git a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts index cd0fc7eae4d..dcf5a720ef3 100644 --- a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts @@ -17,6 +17,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IStorageService } from 'vs/platform/storage/common/storage'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService'; +import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; type ExeExtensionRecommendationsClassification = { extensionId: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; @@ -39,7 +40,8 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { constructor( isExtensionAllowedToBeRecommended: (extensionId: string) => boolean, @IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService, - @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, + @IExtensionManagementService extensionManagementService: IExtensionManagementService, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, @optional(ITASExperimentService) tasExperimentService: ITASExperimentService, @IInstantiationService instantiationService: IInstantiationService, @IConfigurationService configurationService: IConfigurationService, @@ -48,7 +50,7 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { @IStorageService storageService: IStorageService, @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, ) { - super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, storageKeysSyncRegistryService); + super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, extensionsWorkbenchService, extensionManagementService, storageKeysSyncRegistryService); this.tasExperimentService = tasExperimentService; /* diff --git a/src/vs/workbench/contrib/extensions/browser/experimentalRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/experimentalRecommendations.ts index 1e30aee3b93..c24644779fd 100644 --- a/src/vs/workbench/contrib/extensions/browser/experimentalRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/experimentalRecommendations.ts @@ -13,6 +13,8 @@ import { IExperimentService, ExperimentActionType, ExperimentState } from 'vs/wo import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; export class ExperimentalRecommendations extends ExtensionRecommendations { @@ -27,9 +29,11 @@ export class ExperimentalRecommendations extends ExtensionRecommendations { @INotificationService notificationService: INotificationService, @ITelemetryService telemetryService: ITelemetryService, @IStorageService storageService: IStorageService, + @IExtensionManagementService extensionManagementService: IExtensionManagementService, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, ) { - super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, storageKeysSyncRegistryService); + super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, extensionsWorkbenchService, extensionManagementService, storageKeysSyncRegistryService); } /** diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts index 9493fd7a182..045c72e8f42 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts @@ -8,13 +8,15 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { localize } from 'vs/nls'; -import { InstallRecommendedExtensionsAction, SearchExtensionsAction, OpenExtensionEditorAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { SearchExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { ExtensionRecommendationSource, IExtensionRecommendationReson } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { IExtensionsConfiguration, ConfigurationKey } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IExtensionsConfiguration, ConfigurationKey, IExtension, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { IAction } from 'vs/base/common/actions'; +import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { CancellationToken } from 'vs/base/common/cancellation'; type ExtensionRecommendationsNotificationClassification = { userReaction: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; @@ -42,6 +44,8 @@ export abstract class ExtensionRecommendations extends Disposable { @INotificationService protected readonly notificationService: INotificationService, @ITelemetryService protected readonly telemetryService: ITelemetryService, @IStorageService protected readonly storageService: IStorageService, + @IExtensionsWorkbenchService protected readonly extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionManagementService protected readonly extensionManagementService: IExtensionManagementService, @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, ) { super(); @@ -65,22 +69,40 @@ export abstract class ExtensionRecommendations extends Disposable { } } - protected promptImportantExtensionsInstallNotification(extensionIds: string[], message: string, searchValue: string): void { + private async getInstallableExtensions(extensionIds: string[]): Promise { + const extensions: IExtension[] = []; + const pager = await this.extensionsWorkbenchService.queryGallery({ names: extensionIds, pageSize: extensionIds.length, source: 'install-recommendations' }, CancellationToken.None); + for (const extension of pager.firstPage) { + if (extension.gallery && (await this.extensionManagementService.canInstall(extension.gallery))) { + extensions.push(extension); + } + } + return extensions; + } + + protected async promptImportantExtensionsInstallNotification(extensionIds: string[], message: string, searchValue: string): Promise { + const extensions = await this.getInstallableExtensions(extensionIds); + if (!extensions.length) { + return; + } + this.notificationService.prompt(Severity.Info, message, [{ label: localize('install', 'Install'), run: async () => { - for (const extensionId of extensionIds) { - this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'install', extensionId }); - } - this.runAction(this.instantiationService.createInstance(InstallRecommendedExtensionsAction, InstallRecommendedExtensionsAction.ID, InstallRecommendedExtensionsAction.LABEL, extensionIds, searchValue, 'install-recommendations')); + this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); + await Promise.all(extensions.map(async extension => { + this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'install', extensionId: extension.identifier.id }); + this.extensionsWorkbenchService.open(extension, { pinned: true }); + await this.extensionManagementService.installFromGallery(extension.gallery!); + })); } }, { label: localize('show recommendations', "Show Recommendations"), run: async () => { - for (const extensionId of extensionIds) { - this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'show', extensionId }); - this.runAction(this.instantiationService.createInstance(OpenExtensionEditorAction, extensionId)); + for (const extension of extensions) { + this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'show', extensionId: extension.identifier.id }); + this.extensionsWorkbenchService.open(extension, { pinned: true }); } this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); } @@ -88,9 +110,9 @@ export abstract class ExtensionRecommendations extends Disposable { label: choiceNever, isSecondary: true, run: () => { - for (const extensionId of extensionIds) { - this.addToImportantRecommendationsIgnore(extensionId); - this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId }); + for (const extension of extensions) { + this.addToImportantRecommendationsIgnore(extension.identifier.id); + this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId: extension.identifier.id }); } this.notificationService.prompt( Severity.Info, @@ -108,8 +130,8 @@ export abstract class ExtensionRecommendations extends Disposable { { sticky: true, onCancel: () => { - for (const extensionId of extensionIds) { - this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'cancelled', extensionId }); + for (const extension of extensions) { + this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'cancelled', extensionId: extension.identifier.id }); } } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts index 907c686d16d..18d07fed9d4 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts @@ -8,7 +8,7 @@ import { IExtensionManagementService, IExtensionGalleryService, InstallOperation import { IExtensionRecommendationsService, ExtensionRecommendationReason, RecommendationChangeNotification, IExtensionRecommendation, ExtensionRecommendationSource, EnablementState, IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ConfigurationKey, IExtensionsConfiguration, ShowRecommendationsOnlyOnDemandKey } from 'vs/workbench/contrib/extensions/common/extensions'; +import { ConfigurationKey, IExtension, IExtensionsConfiguration, IExtensionsWorkbenchService, ShowRecommendationsOnlyOnDemandKey } from 'vs/workbench/contrib/extensions/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { distinct, shuffle } from 'vs/base/common/arrays'; @@ -29,7 +29,8 @@ import { areSameExtensions } from 'vs/platform/extensionManagement/common/extens import Severity from 'vs/base/common/severity'; import { localize } from 'vs/nls'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { InstallRecommendedExtensionsAction, SearchExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { SearchExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { CancellationToken } from 'vs/base/common/cancellation'; type IgnoreRecommendationClassification = { recommendationReason: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; @@ -75,8 +76,9 @@ export class ExtensionRecommendationsService extends Disposable implements IExte @ITelemetryService private readonly telemetryService: ITelemetryService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, - @IWorkbenchExtensionEnablementService protected readonly extensionEnablementService: IWorkbenchExtensionEnablementService, - @INotificationService protected readonly notificationService: INotificationService, + @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, + @INotificationService private readonly notificationService: INotificationService, + @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, ) { super(); @@ -289,6 +291,17 @@ export class ExtensionRecommendationsService extends Disposable implements IExte return allIgnoredRecommendations.indexOf(id.toLowerCase()) === -1; } + private async getInstallableExtensions(extensionIds: string[]): Promise { + const extensions: IExtension[] = []; + const pager = await this.extensionsWorkbenchService.queryGallery({ names: extensionIds, pageSize: extensionIds.length, source: 'install-recommendations' }, CancellationToken.None); + for (const extension of pager.firstPage) { + if (extension.gallery && (await this.extensionManagementService.canInstall(extension.gallery))) { + extensions.push(extension); + } + } + return extensions; + } + private async promptWorkspaceRecommendations(): Promise { const allowedRecommendations = [...this.workspaceRecommendations.recommendations, ...this.configBasedRecommendations.importantRecommendations] .map(({ extensionId }) => extensionId) @@ -309,6 +322,11 @@ export class ExtensionRecommendationsService extends Disposable implements IExte return; } + const extensions = await this.getInstallableExtensions(recommendations); + if (!extensions.length) { + return; + } + const searchValue = '@recommended '; this.notificationService.prompt( Severity.Info, @@ -317,12 +335,10 @@ export class ExtensionRecommendationsService extends Disposable implements IExte label: localize('install', "Install"), run: async () => { this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'install' }); - const action = this.instantiationService.createInstance(InstallRecommendedExtensionsAction, InstallRecommendedExtensionsAction.ID, InstallRecommendedExtensionsAction.LABEL, recommendations, searchValue, 'install-all-workspace-recommendations'); - try { - await action.run(); - } finally { - action.dispose(); - } + await Promise.all(extensions.map(async extension => { + this.extensionsWorkbenchService.open(extension, { pinned: true }); + await this.extensionManagementService.installFromGallery(extension.gallery!); + })); } }, { label: localize('showRecommendations', "Show Recommendations"), diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 8213b49d616..70f25aa91b9 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -1940,24 +1940,6 @@ export class ShowRecommendedExtensionAction extends Action { } } -export class OpenExtensionEditorAction extends Action { - - constructor( - private readonly extensionId: string, - @IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService, - ) { - super('extensions.openExtension', localize('open extension', "Open Extension"), undefined, true); - } - - async run(): Promise { - const pager = await this.extensionWorkbenchService.queryGallery({ names: [this.extensionId], source: 'install-recommendation', pageSize: 1 }, CancellationToken.None); - if (pager && pager.firstPage && pager.firstPage.length) { - const extension = pager.firstPage[0]; - return this.extensionWorkbenchService.open(extension, { pinned: true }); - } - } -} - export class InstallRecommendedExtensionAction extends Action { static readonly ID = 'workbench.extensions.action.installRecommendedExtension'; diff --git a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts index fda072b2b69..a111db1ad4a 100644 --- a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts @@ -26,6 +26,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { setImmediate } from 'vs/base/common/platform'; +import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; type FileExtensionSuggestionClassification = { userReaction: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; @@ -84,7 +85,8 @@ export class FileBasedRecommendations extends ExtensionRecommendations { constructor( isExtensionAllowedToBeRecommended: (extensionId: string) => boolean, - @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionManagementService extensionManagementService: IExtensionManagementService, @IExtensionService private readonly extensionService: IExtensionService, @IViewletService private readonly viewletService: IViewletService, @IModelService private readonly modelService: IModelService, @@ -96,7 +98,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { @IStorageService storageService: IStorageService, @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, ) { - super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, storageKeysSyncRegistryService); + super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, extensionsWorkbenchService, extensionManagementService, storageKeysSyncRegistryService); if (productService.extensionTips) { forEach(productService.extensionTips, ({ key, value }) => this.extensionTips[key.toLowerCase()] = value); @@ -228,7 +230,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { return false; } - this.promptImportantExtensionsInstallNotification([extensionId], localize('reallyRecommended', "Do you want to install support for {0}?", languageName), `ext:${ext}`); + this.promptImportantExtensionsInstallNotification([extensionId], localize('reallyRecommended', "Do you want to install support for {0}?", languageName), `@id:${extensionId}`); return true; } diff --git a/src/vs/workbench/contrib/extensions/browser/keymapRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/keymapRecommendations.ts index 4bfefe4941c..517086adfa5 100644 --- a/src/vs/workbench/contrib/extensions/browser/keymapRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/keymapRecommendations.ts @@ -12,6 +12,8 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; export class KeymapRecommendations extends ExtensionRecommendations { @@ -26,9 +28,11 @@ export class KeymapRecommendations extends ExtensionRecommendations { @INotificationService notificationService: INotificationService, @ITelemetryService telemetryService: ITelemetryService, @IStorageService storageService: IStorageService, + @IExtensionManagementService extensionManagementService: IExtensionManagementService, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, ) { - super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, storageKeysSyncRegistryService); + super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, extensionsWorkbenchService, extensionManagementService, storageKeysSyncRegistryService); } protected async doActivate(): Promise { diff --git a/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts index 80eab437ff4..ac8603ff2f8 100644 --- a/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EXTENSION_IDENTIFIER_PATTERN, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { EXTENSION_IDENTIFIER_PATTERN, IExtensionGalleryService, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkspaceContextService, IWorkspaceFolder, IWorkspace, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; import { IFileService } from 'vs/platform/files/common/files'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -13,7 +13,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { INotificationService } from 'vs/platform/notification/common/notification'; import { IExtensionsConfigContent, ExtensionRecommendationSource, ExtensionRecommendationReason } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { parse } from 'vs/base/common/json'; -import { EXTENSIONS_CONFIG } from 'vs/workbench/contrib/extensions/common/extensions'; +import { EXTENSIONS_CONFIG, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; import { localize } from 'vs/nls'; @@ -44,9 +44,11 @@ export class WorkspaceRecommendations extends ExtensionRecommendations { @INotificationService notificationService: INotificationService, @ITelemetryService telemetryService: ITelemetryService, @IStorageService storageService: IStorageService, + @IExtensionManagementService extensionManagementService: IExtensionManagementService, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, ) { - super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, storageKeysSyncRegistryService); + super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, extensionsWorkbenchService, extensionManagementService, storageKeysSyncRegistryService); } protected async doActivate(): Promise { From c0a8b1d2dd28a777845700d3033e9e021ec5b2ef Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 27 Aug 2020 14:03:46 +0200 Subject: [PATCH 595/736] do not show not installable extensions in recommendations --- .../browser/exeBasedRecommendations.ts | 42 ++- .../browser/extensionRecommendations.ts | 10 +- .../extensionRecommendationsService.ts | 2 +- .../extensions/browser/extensionsViews.ts | 333 +++++++----------- .../browser/fileBasedRecommendations.ts | 88 ++--- .../extensionRecommendationsService.test.ts | 41 ++- .../electron-browser/extensionsViews.test.ts | 15 +- 7 files changed, 238 insertions(+), 293 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts index dcf5a720ef3..fffefd1f7a8 100644 --- a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts @@ -8,7 +8,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { timeout } from 'vs/base/common/async'; import { localize } from 'vs/nls'; -import { IStringDictionary } from 'vs/base/common/collections'; import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { basename } from 'vs/base/common/path'; @@ -26,7 +25,6 @@ type ExeExtensionRecommendationsClassification = { export class ExeBasedRecommendations extends ExtensionRecommendations { - private _otherTips: IExecutableBasedExtensionTip[] = []; private _importantTips: IExecutableBasedExtensionTip[] = []; @@ -77,18 +75,18 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { await this.fetchImportantExeBasedRecommendations(); } - private _importantExeBasedRecommendations: Promise> | undefined; - private async fetchImportantExeBasedRecommendations(): Promise> { + private _importantExeBasedRecommendations: Promise> | undefined; + private async fetchImportantExeBasedRecommendations(): Promise> { if (!this._importantExeBasedRecommendations) { this._importantExeBasedRecommendations = this.doFetchImportantExeBasedRecommendations(); } return this._importantExeBasedRecommendations; } - private async doFetchImportantExeBasedRecommendations(): Promise> { - const importantExeBasedRecommendations: IStringDictionary = {}; + private async doFetchImportantExeBasedRecommendations(): Promise> { + const importantExeBasedRecommendations = new Map(); this._importantTips = await this.extensionTipsService.getImportantExecutableBasedTips(); - this._importantTips.forEach(tip => importantExeBasedRecommendations[tip.extensionId.toLowerCase()] = tip); + this._importantTips.forEach(tip => importantExeBasedRecommendations.set(tip.extensionId.toLowerCase(), tip)); return importantExeBasedRecommendations; } @@ -96,22 +94,26 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { const importantExeBasedRecommendations = await this.fetchImportantExeBasedRecommendations(); const local = await this.extensionManagementService.getInstalled(); - const { installed, uninstalled } = this.groupByInstalled(Object.keys(importantExeBasedRecommendations), local); + const { installed, uninstalled } = this.groupByInstalled([...importantExeBasedRecommendations.keys()], local); /* Log installed and uninstalled exe based recommendations */ for (const extensionId of installed) { - const tip = importantExeBasedRecommendations[extensionId]; - this.telemetryService.publicLog2<{ exeName: string, extensionId: string }, ExeExtensionRecommendationsClassification>('exeExtensionRecommendations:alreadyInstalled', { extensionId, exeName: basename(tip.windowsPath!) }); + const tip = importantExeBasedRecommendations.get(extensionId); + if (tip) { + this.telemetryService.publicLog2<{ exeName: string, extensionId: string }, ExeExtensionRecommendationsClassification>('exeExtensionRecommendations:alreadyInstalled', { extensionId, exeName: basename(tip.windowsPath!) }); + } } for (const extensionId of uninstalled) { - const tip = importantExeBasedRecommendations[extensionId]; - this.telemetryService.publicLog2<{ exeName: string, extensionId: string }, ExeExtensionRecommendationsClassification>('exeExtensionRecommendations:notInstalled', { extensionId, exeName: basename(tip.windowsPath!) }); + const tip = importantExeBasedRecommendations.get(extensionId); + if (tip) { + this.telemetryService.publicLog2<{ exeName: string, extensionId: string }, ExeExtensionRecommendationsClassification>('exeExtensionRecommendations:notInstalled', { extensionId, exeName: basename(tip.windowsPath!) }); + } } this.promptImportantExeBasedRecommendations(uninstalled, importantExeBasedRecommendations); } - private async promptImportantExeBasedRecommendations(recommendations: string[], importantExeBasedRecommendations: IStringDictionary): Promise { + private async promptImportantExeBasedRecommendations(recommendations: string[], importantExeBasedRecommendations: Map): Promise { if (this.hasToIgnoreRecommendationNotifications()) { return; } @@ -122,13 +124,15 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { const recommendationsByExe = new Map(); for (const extensionId of recommendations) { - const tip = importantExeBasedRecommendations[extensionId]; - let tips = recommendationsByExe.get(tip.exeFriendlyName); - if (!tips) { - tips = []; - recommendationsByExe.set(tip.exeFriendlyName, tips); + const tip = importantExeBasedRecommendations.get(extensionId); + if (tip) { + let tips = recommendationsByExe.get(tip.exeFriendlyName); + if (!tips) { + tips = []; + recommendationsByExe.set(tip.exeFriendlyName, tips); + } + tips.push(tip); } - tips.push(tip); } for (const [, tips] of recommendationsByExe) { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts index 045c72e8f42..caf2db0f27a 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts @@ -71,10 +71,12 @@ export abstract class ExtensionRecommendations extends Disposable { private async getInstallableExtensions(extensionIds: string[]): Promise { const extensions: IExtension[] = []; - const pager = await this.extensionsWorkbenchService.queryGallery({ names: extensionIds, pageSize: extensionIds.length, source: 'install-recommendations' }, CancellationToken.None); - for (const extension of pager.firstPage) { - if (extension.gallery && (await this.extensionManagementService.canInstall(extension.gallery))) { - extensions.push(extension); + if (extensionIds.length) { + const pager = await this.extensionsWorkbenchService.queryGallery({ names: extensionIds, pageSize: extensionIds.length, source: 'install-recommendations' }, CancellationToken.None); + for (const extension of pager.firstPage) { + if (extension.gallery && (await this.extensionManagementService.canInstall(extension.gallery))) { + extensions.push(extension); + } } } return extensions; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts index 18d07fed9d4..5b08837d91c 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts @@ -126,7 +126,7 @@ export class ExtensionRecommendationsService extends Disposable implements IExte }) ]); - this.promptWorkspaceRecommendations(); + await this.promptWorkspaceRecommendations(); this._register(Event.any(this.workspaceRecommendations.onDidChangeRecommendations, this.configBasedRecommendations.onDidChangeRecommendations)(() => this.promptWorkspaceRecommendations())); } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 3cc77454f2b..fb17f22cb23 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -9,7 +9,7 @@ import { assign } from 'vs/base/common/objects'; import { Event, Emitter } from 'vs/base/common/event'; import { isPromiseCanceledError, getErrorMessage } from 'vs/base/common/errors'; import { PagedModel, IPagedModel, IPager, DelayedPagedModel } from 'vs/base/common/paging'; -import { SortBy, SortOrder, IQueryOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { SortBy, SortOrder, IQueryOptions, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionManagementServer, IExtensionManagementServerService, IExtensionRecommendationsService, IExtensionRecommendation, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -31,7 +31,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { distinct, coalesce } from 'vs/base/common/arrays'; +import { coalesce, distinct, flatten } from 'vs/base/common/arrays'; import { IExperimentService, IExperiment, ExperimentActionType } from 'vs/workbench/contrib/experiments/common/experimentService'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; @@ -53,6 +53,10 @@ import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; // Extensions that are automatically classified as Programming Language extensions, but should be Feature extensions const FORCE_FEATURE_EXTENSIONS = ['vscode.git', 'vscode.search-result']; +type WorkspaceRecommendationsClassification = { + count: { classification: 'SystemMetaData', purpose: 'FeatureInsight', 'isMeasurement': true }; +}; + class ExtensionsViewState extends Disposable implements IExtensionsViewState { private readonly _onFocus: Emitter = this._register(new Emitter()); @@ -98,12 +102,13 @@ export class ExtensionsListView extends ViewPane { @IThemeService themeService: IThemeService, @IExtensionService private readonly extensionService: IExtensionService, @IExtensionsWorkbenchService protected extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionRecommendationsService protected tipsService: IExtensionRecommendationsService, + @IExtensionRecommendationsService protected extensionRecommendationsService: IExtensionRecommendationsService, @ITelemetryService telemetryService: ITelemetryService, @IConfigurationService configurationService: IConfigurationService, @IWorkspaceContextService protected contextService: IWorkspaceContextService, @IExperimentService private readonly experimentService: IExperimentService, @IExtensionManagementServerService protected readonly extensionManagementServerService: IExtensionManagementServerService, + @IExtensionManagementService protected readonly extensionManagementService: IExtensionManagementService, @IProductService protected readonly productService: IProductService, @IContextKeyService contextKeyService: IContextKeyService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @@ -456,16 +461,8 @@ export class ExtensionsListView extends ViewPane { options.sortBy = SortBy.InstallCount; } - if (ExtensionsListView.isWorkspaceRecommendedExtensionsQuery(query.value)) { - return this.getWorkspaceRecommendationsModel(query, options, token); - } else if (ExtensionsListView.isKeymapsRecommendedExtensionsQuery(query.value)) { - return this.getKeymapRecommendationsModel(query, options, token); - } else if (ExtensionsListView.isExeRecommendedExtensionsQuery(query.value)) { - return this.getExeRecommendationsModel(query, options, token); - } else if (/@recommended:all/i.test(query.value) || ExtensionsListView.isSearchRecommendedExtensionsQuery(query.value)) { - return this.getAllRecommendationsModel(query, options, token); - } else if (ExtensionsListView.isRecommendedExtensionsQuery(query.value)) { - return this.getRecommendationsModel(query, options, token); + if (this.isRecommendationsQuery(query)) { + return this.queryRecommendations(query, options, token); } if (/\bcurated:([^\s]+)\b/.test(query.value)) { @@ -542,51 +539,6 @@ export class ExtensionsListView extends ViewPane { return extensions; } - // Get All types of recommendations, trimmed to show a max of 8 at any given time - private getAllRecommendationsModel(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { - const value = query.value.replace(/@recommended:all/g, '').replace(/@recommended/g, '').trim().toLowerCase(); - - return this.extensionsWorkbenchService.queryLocal(this.server) - .then(result => result.filter(e => e.type === ExtensionType.User)) - .then(local => { - const fileBasedRecommendations = this.tipsService.getFileBasedRecommendations(); - const configBasedRecommendationsPromise = this.tipsService.getConfigBasedRecommendations(); - const othersPromise = this.tipsService.getOtherRecommendations(); - const workspacePromise = this.tipsService.getWorkspaceRecommendations(); - const importantRecommendationsPromise = this.tipsService.getImportantRecommendations(); - - return Promise.all([othersPromise, workspacePromise, configBasedRecommendationsPromise, importantRecommendationsPromise]) - .then(([others, workspaceRecommendations, configBasedRecommendations, importantRecommendations]) => { - const names = this.getTrimmedRecommendations(local, value, importantRecommendations, fileBasedRecommendations, configBasedRecommendations, others, workspaceRecommendations); - const recommendationsWithReason = this.tipsService.getAllRecommendationsWithReason(); - /* __GDPR__ - "extensionAllRecommendations:open" : { - "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "recommendations": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('extensionAllRecommendations:open', { - count: names.length, - recommendations: names.map(id => { - return { - id, - recommendationReason: recommendationsWithReason[id.toLowerCase()].reasonId - }; - }) - }); - if (!names.length) { - return Promise.resolve(new PagedModel([])); - } - options.source = 'recommendations-all'; - return this.extensionsWorkbenchService.queryGallery(assign(options, { names, pageSize: names.length }), token) - .then(pager => { - this.sortFirstPage(pager, names); - return this.getPagedModel(pager || []); - }); - }); - }); - } - private async getCuratedModel(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { const value = query.value.replace(/curated:/g, '').trim(); const names = await this.experimentService.getCuratedExtensionsList(value); @@ -599,152 +551,128 @@ export class ExtensionsListView extends ViewPane { return new PagedModel([]); } - // Get All types of recommendations other than Workspace recommendations, trimmed to show a max of 8 at any given time - private getRecommendationsModel(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { - const value = query.value.replace(/@recommended/g, '').trim().toLowerCase(); - - return this.extensionsWorkbenchService.queryLocal(this.server) - .then(result => result.filter(e => e.type === ExtensionType.User)) - .then(local => { - let fileBasedRecommendations = this.tipsService.getFileBasedRecommendations(); - const configBasedRecommendationsPromise = this.tipsService.getConfigBasedRecommendations(); - const othersPromise = this.tipsService.getOtherRecommendations(); - const workspacePromise = this.tipsService.getWorkspaceRecommendations(); - const importantRecommendationsPromise = this.tipsService.getImportantRecommendations(); - - return Promise.all([othersPromise, workspacePromise, configBasedRecommendationsPromise, importantRecommendationsPromise]) - .then(([others, workspaceRecommendations, configBasedRecommendations, importantRecommendations]) => { - configBasedRecommendations = configBasedRecommendations.filter(x => workspaceRecommendations.every(({ extensionId }) => x.extensionId !== extensionId)); - fileBasedRecommendations = fileBasedRecommendations.filter(x => workspaceRecommendations.every(({ extensionId }) => x.extensionId !== extensionId)); - others = others.filter(x => workspaceRecommendations.every(({ extensionId }) => x.extensionId !== extensionId)); - - const names = this.getTrimmedRecommendations(local, value, importantRecommendations, fileBasedRecommendations, configBasedRecommendations, others, []); - const recommendationsWithReason = this.tipsService.getAllRecommendationsWithReason(); - - /* __GDPR__ - "extensionRecommendations:open" : { - "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "recommendations": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('extensionRecommendations:open', { - count: names.length, - recommendations: names.map(id => { - return { - id, - recommendationReason: recommendationsWithReason[id.toLowerCase()].reasonId - }; - }) - }); - - if (!names.length) { - return Promise.resolve(new PagedModel([])); - } - options.source = 'recommendations'; - return this.extensionsWorkbenchService.queryGallery(assign(options, { names, pageSize: names.length }), token) - .then(pager => { - this.sortFirstPage(pager, names); - return this.getPagedModel(pager || []); - }); - }); - }); + private isRecommendationsQuery(query: Query): boolean { + return ExtensionsListView.isWorkspaceRecommendedExtensionsQuery(query.value) + || ExtensionsListView.isKeymapsRecommendedExtensionsQuery(query.value) + || ExtensionsListView.isExeRecommendedExtensionsQuery(query.value) + || /@recommended:all/i.test(query.value) + || ExtensionsListView.isSearchRecommendedExtensionsQuery(query.value) + || ExtensionsListView.isRecommendedExtensionsQuery(query.value); } - // Given all recommendations, trims and returns recommendations in the relevant order after filtering out installed extensions - private getTrimmedRecommendations(installedExtensions: IExtension[], value: string, importantRecommendations: IExtensionRecommendation[], fileBasedRecommendations: IExtensionRecommendation[], configBasedRecommendations: IExtensionRecommendation[], otherRecommendations: IExtensionRecommendation[], workspaceRecommendations: IExtensionRecommendation[]): string[] { - const totalCount = 10; - workspaceRecommendations = workspaceRecommendations - .filter(recommendation => { - return !this.isRecommendationInstalled(recommendation, installedExtensions) - && recommendation.extensionId.toLowerCase().indexOf(value) > -1; - }); - importantRecommendations = importantRecommendations - .filter(recommendation => { - return !this.isRecommendationInstalled(recommendation, installedExtensions) - && workspaceRecommendations.every(workspaceRecommendation => workspaceRecommendation.extensionId !== recommendation.extensionId) - && recommendation.extensionId.toLowerCase().indexOf(value) > -1; - }); - configBasedRecommendations = configBasedRecommendations - .filter(recommendation => { - return !this.isRecommendationInstalled(recommendation, installedExtensions) - && workspaceRecommendations.every(workspaceRecommendation => workspaceRecommendation.extensionId !== recommendation.extensionId) - && importantRecommendations.every(importantRecommendation => importantRecommendation.extensionId !== recommendation.extensionId) - && recommendation.extensionId.toLowerCase().indexOf(value) > -1; - }); - fileBasedRecommendations = fileBasedRecommendations.filter(recommendation => { - return !this.isRecommendationInstalled(recommendation, installedExtensions) - && workspaceRecommendations.every(workspaceRecommendation => workspaceRecommendation.extensionId !== recommendation.extensionId) - && importantRecommendations.every(importantRecommendation => importantRecommendation.extensionId !== recommendation.extensionId) - && configBasedRecommendations.every(configBasedRecommendation => configBasedRecommendation.extensionId !== recommendation.extensionId) - && recommendation.extensionId.toLowerCase().indexOf(value) > -1; - }); - otherRecommendations = otherRecommendations.filter(recommendation => { - return !this.isRecommendationInstalled(recommendation, installedExtensions) - && fileBasedRecommendations.every(fileBasedRecommendation => fileBasedRecommendation.extensionId !== recommendation.extensionId) - && workspaceRecommendations.every(workspaceRecommendation => workspaceRecommendation.extensionId !== recommendation.extensionId) - && importantRecommendations.every(importantRecommendation => importantRecommendation.extensionId !== recommendation.extensionId) - && configBasedRecommendations.every(configBasedRecommendation => configBasedRecommendation.extensionId !== recommendation.extensionId) - && recommendation.extensionId.toLowerCase().indexOf(value) > -1; - }); - - const otherCount = Math.min(2, otherRecommendations.length); - const fileBasedCount = Math.min(fileBasedRecommendations.length, totalCount - workspaceRecommendations.length - importantRecommendations.length - configBasedRecommendations.length - otherCount); - const recommendations = [...workspaceRecommendations, ...importantRecommendations, ...configBasedRecommendations]; - recommendations.push(...fileBasedRecommendations.splice(0, fileBasedCount)); - recommendations.push(...otherRecommendations.splice(0, otherCount)); - - return distinct(recommendations.map(({ extensionId }) => extensionId)); - } - - private isRecommendationInstalled(recommendation: IExtensionRecommendation, installed: IExtension[]): boolean { - return installed.some(i => areSameExtensions(i.identifier, { id: recommendation.extensionId })); - } - - private getWorkspaceRecommendationsModel(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { - const value = query.value.replace(/@recommended:workspace/g, '').trim().toLowerCase(); - return this.tipsService.getWorkspaceRecommendations() - .then(recommendations => { - const names = recommendations.map(({ extensionId }) => extensionId).filter(name => name.toLowerCase().indexOf(value) > -1); - /* __GDPR__ - "extensionWorkspaceRecommendations:open" : { - "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ - this.telemetryService.publicLog('extensionWorkspaceRecommendations:open', { count: names.length }); - - if (!names.length) { - return Promise.resolve(new PagedModel([])); - } - options.source = 'recommendations-workspace'; - return this.extensionsWorkbenchService.queryGallery(assign(options, { names, pageSize: names.length }), token) - .then(pager => this.getPagedModel(pager || [])); - }); - } - - private getKeymapRecommendationsModel(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { - const value = query.value.replace(/@recommended:keymaps/g, '').trim().toLowerCase(); - const names: string[] = this.tipsService.getKeymapRecommendations().map(({ extensionId }) => extensionId) - .filter(extensionId => extensionId.toLowerCase().indexOf(value) > -1); - - if (!names.length) { - return Promise.resolve(new PagedModel([])); + private async queryRecommendations(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { + // Workspace recommendations + if (ExtensionsListView.isWorkspaceRecommendedExtensionsQuery(query.value)) { + return this.getWorkspaceRecommendationsModel(query, options, token); } - options.source = 'recommendations-keymaps'; - return this.extensionsWorkbenchService.queryGallery(assign(options, { names, pageSize: names.length }), token) - .then(result => this.getPagedModel(result)); + + // Keymap recommendations + if (ExtensionsListView.isKeymapsRecommendedExtensionsQuery(query.value)) { + return this.getKeymapRecommendationsModel(query, options, token); + } + + // Exe recommendations + if (ExtensionsListView.isExeRecommendedExtensionsQuery(query.value)) { + return this.getExeRecommendationsModel(query, options, token); + } + + // All recommendations + if (/@recommended:all/i.test(query.value) || ExtensionsListView.isSearchRecommendedExtensionsQuery(query.value)) { + return this.getAllRecommendationsModel(query, options, token); + } + + // Other recommendations + if (ExtensionsListView.isRecommendedExtensionsQuery(query.value)) { + return this.getOtherRecommendationsModel(query, options, token); + } + + return new PagedModel([]); + } + + private async getInstallableRecommendations(recommendations: IExtensionRecommendation[], options: IQueryOptions, token: CancellationToken): Promise { + const extensions: IExtension[] = []; + if (recommendations.length) { + const names = recommendations.map(({ extensionId }) => extensionId); + const pager = await this.extensionsWorkbenchService.queryGallery({ ...options, names, pageSize: names.length }, token); + for (const extension of pager.firstPage) { + if (extension.gallery && (await this.extensionManagementService.canInstall(extension.gallery))) { + extensions.push(extension); + } + } + } + return extensions; + } + + private async getWorkspaceRecommendationsModel(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { + const value = query.value.replace(/@recommended:workspace/g, '').trim().toLowerCase(); + const recommendations = await this.extensionRecommendationsService.getWorkspaceRecommendations(); + const installableRecommendations = (await this.getInstallableRecommendations(recommendations, { ...options, source: 'recommendations-workspace' }, token)) + .filter(extension => extension.identifier.id.toLowerCase().indexOf(value) > -1); + this.telemetryService.publicLog2<{ count: number }, WorkspaceRecommendationsClassification>('extensionWorkspaceRecommendations:open', { count: installableRecommendations.length }); + return new PagedModel(installableRecommendations); + } + + private async getKeymapRecommendationsModel(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { + const value = query.value.replace(/@recommended:keymaps/g, '').trim().toLowerCase(); + const recommendations = this.extensionRecommendationsService.getKeymapRecommendations(); + const installableRecommendations = (await this.getInstallableRecommendations(recommendations, { ...options, source: 'recommendations-keymaps' }, token)) + .filter(extension => extension.identifier.id.toLowerCase().indexOf(value) > -1); + return new PagedModel(installableRecommendations); } private async getExeRecommendationsModel(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { const exe = query.value.replace(/@exe:/g, '').trim().toLowerCase(); - const { important, others } = await this.tipsService.getExeBasedRecommendations(exe.startsWith('"') ? exe.substring(1, exe.length - 1) : exe); - const names: string[] = [...important, ...others].map(({ extensionId }) => extensionId); + const { important, others } = await this.extensionRecommendationsService.getExeBasedRecommendations(exe.startsWith('"') ? exe.substring(1, exe.length - 1) : exe); + const installableRecommendations = await this.getInstallableRecommendations([...important, ...others], { ...options, source: 'recommendations-exe' }, token); + return new PagedModel(installableRecommendations); + } - if (!names.length) { - return Promise.resolve(new PagedModel([])); - } - options.source = 'recommendations-exe'; - return this.extensionsWorkbenchService.queryGallery(assign(options, { names, pageSize: names.length }), token) - .then(result => this.getPagedModel(result)); + private async getOtherRecommendationsModel(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { + const value = query.value.replace(/@recommended/g, '').trim().toLowerCase(); + + const local = (await this.extensionsWorkbenchService.queryLocal(this.server)) + .filter(e => e.type === ExtensionType.User) + .map(e => e.identifier.id.toLowerCase()); + const workspaceRecommendations = (await this.extensionRecommendationsService.getWorkspaceRecommendations()) + .map(r => r.extensionId.toLowerCase()); + + const otherRecommendations = distinct( + flatten(await Promise.all([ + // Order is important + this.extensionRecommendationsService.getImportantRecommendations(), + this.extensionRecommendationsService.getConfigBasedRecommendations(), + this.extensionRecommendationsService.getFileBasedRecommendations(), + this.extensionRecommendationsService.getOtherRecommendations() + ])).filter(({ extensionId }) => !local.includes(extensionId.toLowerCase()) && !workspaceRecommendations.includes(extensionId.toLowerCase()) + ), r => r.extensionId.toLowerCase()); + + const installableRecommendations = (await this.getInstallableRecommendations(otherRecommendations, { ...options, source: 'recommendations-other', sortBy: undefined }, token)) + .filter(extension => extension.identifier.id.toLowerCase().indexOf(value) > -1); + + const result: IExtension[] = coalesce(otherRecommendations.map(({ extensionId: id }) => installableRecommendations.find(i => areSameExtensions(i.identifier, { id })))); + return new PagedModel(result); + } + + // Get All types of recommendations, trimmed to show a max of 8 at any given time + private async getAllRecommendationsModel(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { + const local = (await this.extensionsWorkbenchService.queryLocal(this.server)) + .filter(e => e.type === ExtensionType.User) + .map(e => e.identifier.id.toLowerCase()); + + const allRecommendations = distinct( + flatten(await Promise.all([ + // Order is important + this.extensionRecommendationsService.getWorkspaceRecommendations(), + this.extensionRecommendationsService.getImportantRecommendations(), + this.extensionRecommendationsService.getConfigBasedRecommendations(), + this.extensionRecommendationsService.getFileBasedRecommendations(), + this.extensionRecommendationsService.getOtherRecommendations() + ])).filter(({ extensionId }) => !local.includes(extensionId.toLowerCase()) + ), r => r.extensionId.toLowerCase()); + + const installableRecommendations = await this.getInstallableRecommendations(allRecommendations, { ...options, source: 'recommendations-all', sortBy: undefined }, token); + const result: IExtension[] = coalesce(allRecommendations.map(({ extensionId: id }) => installableRecommendations.find(i => areSameExtensions(i.identifier, { id })))); + return new PagedModel(result.slice(0, 8)); } // Sorts the firstPage of the pager in the same order as given array of extension ids @@ -919,6 +847,7 @@ export class ServerExtensionsView extends ExtensionsListView { @IExperimentService experimentService: IExperimentService, @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, @IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService, + @IExtensionManagementService extensionManagementService: IExtensionManagementService, @IProductService productService: IProductService, @IContextKeyService contextKeyService: IContextKeyService, @IMenuService menuService: IMenuService, @@ -927,7 +856,9 @@ export class ServerExtensionsView extends ExtensionsListView { @IPreferencesService preferencesService: IPreferencesService, ) { options.server = server; - super(options, notificationService, keybindingService, contextMenuService, instantiationService, themeService, extensionService, extensionsWorkbenchService, tipsService, telemetryService, configurationService, contextService, experimentService, extensionManagementServerService, productService, contextKeyService, viewDescriptorService, menuService, openerService, preferencesService); + super(options, notificationService, keybindingService, contextMenuService, instantiationService, themeService, extensionService, extensionsWorkbenchService, tipsService, + telemetryService, configurationService, contextService, experimentService, extensionManagementServerService, extensionManagementService, productService, + contextKeyService, viewDescriptorService, menuService, openerService, preferencesService); this._register(onDidChangeTitle(title => this.updateTitle(title))); } @@ -1011,7 +942,7 @@ export class DefaultRecommendedExtensionsView extends ExtensionsListView { renderBody(container: HTMLElement): void { super.renderBody(container); - this._register(this.tipsService.onRecommendationChange(() => { + this._register(this.extensionRecommendationsService.onRecommendationChange(() => { this.show(''); })); } @@ -1036,7 +967,7 @@ export class RecommendedExtensionsView extends ExtensionsListView { renderBody(container: HTMLElement): void { super.renderBody(container); - this._register(this.tipsService.onRecommendationChange(() => { + this._register(this.extensionRecommendationsService.onRecommendationChange(() => { this.show(''); })); } @@ -1053,7 +984,7 @@ export class WorkspaceRecommendedExtensionsView extends ExtensionsListView { renderBody(container: HTMLElement): void { super.renderBody(container); - this._register(this.tipsService.onRecommendationChange(() => this.update())); + this._register(this.extensionRecommendationsService.onRecommendationChange(() => this.update())); this._register(this.extensionsWorkbenchService.onChange(() => this.setRecommendationsToInstall())); this._register(this.contextService.onDidChangeWorkbenchState(() => this.update())); } @@ -1089,7 +1020,7 @@ export class WorkspaceRecommendedExtensionsView extends ExtensionsListView { } private getRecommendationsToInstall(): Promise { - return this.tipsService.getWorkspaceRecommendations() + return this.extensionRecommendationsService.getWorkspaceRecommendations() .then(recommendations => recommendations.filter(({ extensionId }) => { const extension = this.extensionsWorkbenchService.local.filter(i => areSameExtensions({ id: extensionId }, i.identifier))[0]; if (!extension diff --git a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts index a111db1ad4a..8297fbc5b64 100644 --- a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts @@ -40,28 +40,28 @@ const processedFileExtensions: string[] = []; export class FileBasedRecommendations extends ExtensionRecommendations { - private readonly extensionTips: IStringDictionary = Object.create(null); - private readonly importantExtensionTips: IStringDictionary<{ name: string; pattern: string; isExtensionPack?: boolean }> = Object.create(null); + private readonly extensionTips = new Map(); + private readonly importantExtensionTips = new Map(); - private fileBasedRecommendationsByPattern: IStringDictionary = Object.create(null); - private fileBasedRecommendations: IStringDictionary<{ recommendedTime: number, sources: ExtensionRecommendationSource[] }> = Object.create(null); + private readonly fileBasedRecommendationsByPattern = new Map(); + private readonly fileBasedRecommendations = new Map(); get recommendations(): ReadonlyArray { const recommendations: ExtensionRecommendation[] = []; - Object.keys(this.fileBasedRecommendations) + [...this.fileBasedRecommendations.keys()] .sort((a, b) => { - if (this.fileBasedRecommendations[a].recommendedTime === this.fileBasedRecommendations[b].recommendedTime) { - if (this.importantExtensionTips[a]) { + if (this.fileBasedRecommendations.get(a)!.recommendedTime === this.fileBasedRecommendations.get(b)!.recommendedTime) { + if (this.importantExtensionTips.has(a)) { return -1; } - if (this.importantExtensionTips[b]) { + if (this.importantExtensionTips.has(b)) { return 1; } } - return this.fileBasedRecommendations[a].recommendedTime > this.fileBasedRecommendations[b].recommendedTime ? -1 : 1; + return this.fileBasedRecommendations.get(a)!.recommendedTime > this.fileBasedRecommendations.get(b)!.recommendedTime ? -1 : 1; }) .forEach(extensionId => { - for (const source of this.fileBasedRecommendations[extensionId].sources) { + for (const source of this.fileBasedRecommendations.get(extensionId)!.sources) { recommendations.push({ extensionId, source, @@ -76,11 +76,11 @@ export class FileBasedRecommendations extends ExtensionRecommendations { } get importantRecommendations(): ReadonlyArray { - return this.recommendations.filter(e => this.importantExtensionTips[e.extensionId]); + return this.recommendations.filter(e => this.importantExtensionTips.has(e.extensionId)); } get otherRecommendations(): ReadonlyArray { - return this.recommendations.filter(e => !this.importantExtensionTips[e.extensionId]); + return this.recommendations.filter(e => !this.importantExtensionTips.has(e.extensionId)); } constructor( @@ -101,10 +101,10 @@ export class FileBasedRecommendations extends ExtensionRecommendations { super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, extensionsWorkbenchService, extensionManagementService, storageKeysSyncRegistryService); if (productService.extensionTips) { - forEach(productService.extensionTips, ({ key, value }) => this.extensionTips[key.toLowerCase()] = value); + forEach(productService.extensionTips, ({ key, value }) => this.extensionTips.set(key.toLowerCase(), value)); } if (productService.extensionImportantTips) { - forEach(productService.extensionImportantTips, ({ key, value }) => this.importantExtensionTips[key.toLowerCase()] = value); + forEach(productService.extensionImportantTips, ({ key, value }) => this.importantExtensionTips.set(key.toLowerCase(), value)); } } @@ -112,18 +112,18 @@ export class FileBasedRecommendations extends ExtensionRecommendations { const allRecommendations: string[] = []; // group extension recommendations by pattern, like {**/*.md} -> [ext.foo1, ext.bar2] - forEach(this.extensionTips, ({ key: extensionId, value: pattern }) => { - const ids = this.fileBasedRecommendationsByPattern[pattern] || []; + for (const [extensionId, pattern] of this.extensionTips) { + const ids = this.fileBasedRecommendationsByPattern.get(pattern) || []; ids.push(extensionId); - this.fileBasedRecommendationsByPattern[pattern] = ids; + this.fileBasedRecommendationsByPattern.set(pattern, ids); allRecommendations.push(extensionId); - }); - forEach(this.importantExtensionTips, ({ key: extensionId, value }) => { - const ids = this.fileBasedRecommendationsByPattern[value.pattern] || []; + } + for (const [extensionId, value] of this.importantExtensionTips) { + const ids = this.fileBasedRecommendationsByPattern.get(value.pattern) || []; ids.push(extensionId); - this.fileBasedRecommendationsByPattern[value.pattern] = ids; + this.fileBasedRecommendationsByPattern.set(value.pattern, ids); allRecommendations.push(extensionId); - }); + } const cachedRecommendations = this.getCachedRecommendations(); const now = Date.now(); @@ -131,7 +131,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { forEach(cachedRecommendations, ({ key, value }) => { const diff = (now - value) / milliSecondsInADay; if (diff <= 7 && allRecommendations.indexOf(key) > -1) { - this.fileBasedRecommendations[key] = { recommendedTime: value, sources: ['cached'] }; + this.fileBasedRecommendations.set(key.toLowerCase(), { recommendedTime: value, sources: ['cached'] }); } }); @@ -164,25 +164,27 @@ export class FileBasedRecommendations extends ExtensionRecommendations { private async promptRecommendations(uri: URI, fileExtension: string): Promise { const recommendationsToPrompt: { extensionId: string, languageName: string }[] = []; - forEach(this.fileBasedRecommendationsByPattern, ({ key: pattern, value: extensionIds }) => { - if (match(pattern, uri.toString())) { - for (const extensionId of extensionIds) { - // Add to recommendation to prompt if it is an important tip - // Only prompt if the pattern matches the extensionImportantTips pattern - // Otherwise, assume pattern is from extensionTips, which means it should be a file based "passive" recommendation - if (this.importantExtensionTips[extensionId]?.pattern === pattern) { - recommendationsToPrompt.push({ extensionId, languageName: this.importantExtensionTips[extensionId].name }); - } - // Update file based recommendations - const filedBasedRecommendation = this.fileBasedRecommendations[extensionId] || { recommendedTime: Date.now(), sources: [] }; - filedBasedRecommendation.recommendedTime = Date.now(); - if (!filedBasedRecommendation.sources.some(s => s instanceof URI && s.toString() === uri.toString())) { - filedBasedRecommendation.sources.push(uri); - } - this.fileBasedRecommendations[extensionId.toLowerCase()] = filedBasedRecommendation; - } + for (const { 0: pattern, 1: extensionIds } of this.fileBasedRecommendationsByPattern) { + if (!match(pattern, uri.toString())) { + continue; } - }); + for (const extensionId of extensionIds) { + // Add to recommendation to prompt if it is an important tip + // Only prompt if the pattern matches the extensionImportantTips pattern + // Otherwise, assume pattern is from extensionTips, which means it should be a file based "passive" recommendation + if (this.importantExtensionTips.get(extensionId)?.pattern === pattern) { + recommendationsToPrompt.push({ extensionId, languageName: this.importantExtensionTips.get(extensionId)!.name }); + } + + // Update file based recommendations + const filedBasedRecommendation = this.fileBasedRecommendations.get(extensionId) || { recommendedTime: Date.now(), sources: [] }; + filedBasedRecommendation.recommendedTime = Date.now(); + if (!filedBasedRecommendation.sources.some(s => s instanceof URI && s.toString() === uri.toString())) { + filedBasedRecommendation.sources.push(uri); + } + this.fileBasedRecommendations.set(extensionId, filedBasedRecommendation); + } + } this.storeCachedRecommendations(); @@ -225,7 +227,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { } const extensionId = recommendations[0]; - const entry = this.importantExtensionTips[extensionId]; + const entry = this.importantExtensionTips.get(extensionId); if (!entry) { return false; } @@ -312,7 +314,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { private storeCachedRecommendations(): void { const storedRecommendations: IStringDictionary = {}; - forEach(this.fileBasedRecommendations, ({ key, value }) => storedRecommendations[key] = value.recommendedTime); + this.fileBasedRecommendations.forEach((value, key) => storedRecommendations[key] = value.recommendedTime); this.storageService.store(recommendationsStorageKey, JSON.stringify(storedRecommendations), StorageScope.GLOBAL); } } diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts index e49f190ae1e..be5ec682bc4 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts @@ -33,8 +33,7 @@ import { IPager } from 'vs/base/common/paging'; import { assign } from 'vs/base/common/objects'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ConfigurationKey } from 'vs/workbench/contrib/extensions/common/extensions'; -import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; +import { ConfigurationKey, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test'; import { IURLService } from 'vs/platform/url/common/url'; import { ITextModel } from 'vs/editor/common/model'; @@ -58,6 +57,7 @@ import { ExtensionRecommendationsService } from 'vs/workbench/contrib/extensions import { NoOpWorkspaceTagsService } from 'vs/workbench/contrib/tags/browser/workspaceTagsService'; import { IWorkspaceTagsService } from 'vs/workbench/contrib/tags/common/workspaceTags'; import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'; const mockExtensionGallery: IGalleryExtension[] = [ aGalleryExtension('MockExtension1', { @@ -199,11 +199,15 @@ suite('ExtensionRecommendationsService Test', () => { testConfigurationService = new TestConfigurationService(); instantiationService.stub(IConfigurationService, testConfigurationService); instantiationService.stub(INotificationService, new TestNotificationService()); - instantiationService.stub(IExtensionManagementService, ExtensionManagementService); - instantiationService.stub(IExtensionManagementService, 'onInstallExtension', installEvent.event); - instantiationService.stub(IExtensionManagementService, 'onDidInstallExtension', didInstallEvent.event); - instantiationService.stub(IExtensionManagementService, 'onUninstallExtension', uninstallEvent.event); - instantiationService.stub(IExtensionManagementService, 'onDidUninstallExtension', didUninstallEvent.event); + instantiationService.stub(IExtensionManagementService, >{ + onInstallExtension: installEvent.event, + onDidInstallExtension: didInstallEvent.event, + onUninstallExtension: uninstallEvent.event, + onDidUninstallExtension: didUninstallEvent.event, + async getInstalled() { return []; }, + async canInstall() { return true; }, + async getExtensionsReport() { return []; }, + }); instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(IURLService, NativeURLService); @@ -231,6 +235,7 @@ suite('ExtensionRecommendationsService Test', () => { experimentService = instantiationService.createInstance(TestExperimentService); instantiationService.stub(IExperimentService, experimentService); + instantiationService.set(IExtensionsWorkbenchService, instantiationService.createInstance(ExtensionsWorkbenchService)); instantiationService.stub(IExtensionTipsService, instantiationService.createInstance(ExtensionTipsService)); onModelAddedEvent = new Emitter(); @@ -338,20 +343,18 @@ suite('ExtensionRecommendationsService Test', () => { return testNoPromptForValidRecommendations([]); }); - test('ExtensionRecommendationsService: Prompt for valid workspace recommendations', () => { - return setUpFolderWorkspace('myFolder', mockTestData.recommendedExtensions).then(() => { - testObject = instantiationService.createInstance(ExtensionRecommendationsService); - return testObject.activationPromise.then(() => { - const recommendations = Object.keys(testObject.getAllRecommendationsWithReason()); + test('ExtensionRecommendationsService: Prompt for valid workspace recommendations', async () => { + await setUpFolderWorkspace('myFolder', mockTestData.recommendedExtensions); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); + await testObject.activationPromise; - assert.equal(recommendations.length, mockTestData.validRecommendedExtensions.length); - mockTestData.validRecommendedExtensions.forEach(x => { - assert.equal(recommendations.indexOf(x.toLowerCase()) > -1, true); - }); - - assert.ok(prompted); - }); + const recommendations = Object.keys(testObject.getAllRecommendationsWithReason()); + assert.equal(recommendations.length, mockTestData.validRecommendedExtensions.length); + mockTestData.validRecommendedExtensions.forEach(x => { + assert.equal(recommendations.indexOf(x.toLowerCase()) > -1, true); }); + + assert.ok(prompted); }); test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations if they are already installed', () => { diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts index 4bd61fe7b51..6c077780df1 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts @@ -16,7 +16,6 @@ import { } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IExtensionRecommendationsService, ExtensionRecommendationReason, IExtensionRecommendation } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IURLService } from 'vs/platform/url/common/url'; @@ -88,11 +87,15 @@ suite('ExtensionsListView Tests', () => { instantiationService.stub(ISharedProcessService, TestSharedProcessService); instantiationService.stub(IExperimentService, ExperimentService); - instantiationService.stub(IExtensionManagementService, ExtensionManagementService); - instantiationService.stub(IExtensionManagementService, 'onInstallExtension', installEvent.event); - instantiationService.stub(IExtensionManagementService, 'onDidInstallExtension', didInstallEvent.event); - instantiationService.stub(IExtensionManagementService, 'onUninstallExtension', uninstallEvent.event); - instantiationService.stub(IExtensionManagementService, 'onDidUninstallExtension', didUninstallEvent.event); + instantiationService.stub(IExtensionManagementService, >{ + onInstallExtension: installEvent.event, + onDidInstallExtension: didInstallEvent.event, + onUninstallExtension: uninstallEvent.event, + onDidUninstallExtension: didUninstallEvent.event, + async getInstalled() { return []; }, + async canInstall() { return true; }, + async getExtensionsReport() { return []; }, + }); instantiationService.stub(IRemoteAgentService, RemoteAgentService); instantiationService.stub(IContextKeyService, new MockContextKeyService()); instantiationService.stub(IMenuService, new TestMenuService()); From de8eb7dd8cd79be5f83653c09294b9eeb6bbee73 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 27 Aug 2020 14:44:45 +0200 Subject: [PATCH 596/736] use recommendations in prompt text --- .../contrib/extensions/browser/exeBasedRecommendations.ts | 2 +- .../extensions/browser/extensionRecommendationsService.ts | 2 +- .../contrib/extensions/browser/fileBasedRecommendations.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts index fffefd1f7a8..52b4b91a0c6 100644 --- a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts @@ -141,7 +141,7 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { await this.tasExperimentService.getTreatment('wslpopupaa'); } - const message = localize('exeRecommended', "You have {0} installed on your system. Do you want to install support for it?", tips[0].exeFriendlyName); + const message = localize('exeRecommended', "You have {0} installed on your system. Do you want to install recommendations for it?", tips[0].exeFriendlyName); this.promptImportantExtensionsInstallNotification(extensionIds, message, `@exe:"${tips[0].exeName}"`); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts index 5b08837d91c..7a04a1f71c2 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts @@ -330,7 +330,7 @@ export class ExtensionRecommendationsService extends Disposable implements IExte const searchValue = '@recommended '; this.notificationService.prompt( Severity.Info, - localize('workspaceRecommended', "Do you want to install support for this workspace?"), + localize('workspaceRecommended', "Do you want to install recommendations for this repository?"), [{ label: localize('install', "Install"), run: async () => { diff --git a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts index 8297fbc5b64..6c3ed3be9d3 100644 --- a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts @@ -232,7 +232,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { return false; } - this.promptImportantExtensionsInstallNotification([extensionId], localize('reallyRecommended', "Do you want to install support for {0}?", languageName), `@id:${extensionId}`); + this.promptImportantExtensionsInstallNotification([extensionId], localize('reallyRecommended', "Do you want to install recommendations for {0}?", languageName), `@id:${extensionId}`); return true; } From dcdc03f35d3e466a2e8a17b4bf5351aa6bcf598d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 27 Aug 2020 14:00:35 +0200 Subject: [PATCH 597/736] activate extensions before trying to resolve a notebook --- .../contrib/notebook/browser/notebookServiceImpl.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 80598b122bb..b994093ec72 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -656,9 +656,12 @@ export class NotebookService extends Disposable implements INotebookService, ICu } async resolveNotebook(viewType: string, uri: URI, forceReload: boolean, editorId?: string, backupId?: string): Promise { + + await this.canResolve(viewType); + const provider = this._notebookProviders.get(viewType); if (!provider) { - return undefined; + throw new Error(`CANNOT load notebook, no provider for '${viewType}'`); } let notebookModel: NotebookTextModel | undefined = undefined; From d82329c55d9b765646c729659ccc5fa23cac1756 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 27 Aug 2020 16:08:53 +0200 Subject: [PATCH 598/736] update distro --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f9a659186e8..4cf72416861 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.49.0", - "distro": "7a9bb68effb9d25a544159a0c85e3c20c4ba852e", + "distro": "0891ad485f85afec1bf016d33948b877f5562476", "author": { "name": "Microsoft Corporation" }, @@ -191,4 +191,4 @@ "windows-mutex": "0.3.0", "windows-process-tree": "0.2.4" } -} +} \ No newline at end of file From 874c98d88499695c9081693ee56ec6dfbdadb8e3 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 27 Aug 2020 16:16:06 +0200 Subject: [PATCH 599/736] Always use new hover in custom tree views --- src/vs/workbench/contrib/views/browser/treeView.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/views/browser/treeView.ts b/src/vs/workbench/contrib/views/browser/treeView.ts index a44f72ae44c..0a6fa487993 100644 --- a/src/vs/workbench/contrib/views/browser/treeView.ts +++ b/src/vs/workbench/contrib/views/browser/treeView.ts @@ -766,7 +766,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer('explorer.decorations'); templateData.resourceLabel.setResource({ name: label, description, resource: resource ? resource : URI.parse('missing:_icon_resource') }, { fileKind: this.getFileKind(node), - title, + title: undefined, hideIcon: !!iconUrl, fileDecorations, extraClasses: ['custom-view-tree-node-item-resourceLabel'], @@ -775,7 +775,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer { - await resolvableNode.resolve(); - const tooltip = resolvableNode.tooltip ?? label; + if (node instanceof ResolvableTreeItem) { + await node.resolve(); + } + const tooltip = node.tooltip ?? label; if (isHovering && tooltip) { if (!hoverOptions) { const target: IHoverTarget = { From 60efb7413348920239825a275a34e45a9f02879b Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 27 Aug 2020 16:25:29 +0200 Subject: [PATCH 600/736] update native-keymap --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 4cf72416861..65ae6ee27f3 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "keytar": "^5.5.0", "minimist": "^1.2.5", "native-is-elevated": "0.4.1", - "native-keymap": "2.1.2", + "native-keymap": "2.2.0", "native-watchdog": "1.3.0", "node-pty": "0.10.0-beta8", "semver-umd": "^5.5.7", diff --git a/yarn.lock b/yarn.lock index ef6d7c5760e..07c68d7b74f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6237,10 +6237,10 @@ native-is-elevated@0.4.1: resolved "https://registry.yarnpkg.com/native-is-elevated/-/native-is-elevated-0.4.1.tgz#f6391aafb13441f5b949b39ae0b466b06e7f3986" integrity sha512-2vBXCXCXYKLDjP0WzrXs/AFjDb2njPR31EbGiZ1mR2fMJg211xClK1Xm19RXve35kvAL4dBKOFGCMIyc2+pPsw== -native-keymap@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/native-keymap/-/native-keymap-2.1.2.tgz#9773313f619d4c2b66b452cf036310a145523b59" - integrity sha512-n+oe+sxaauCFxomkl9Xrw1iUp88jTamMaGJSHNSGZ8rkIN9N+Wi6KIvBO8x3nmFxLI27KWu1d8IrLBxFKPNQag== +native-keymap@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/native-keymap/-/native-keymap-2.2.0.tgz#940aeb4ae05776dd44dbd9a80dba5342fd49fb8c" + integrity sha512-rWT9mf5f4vMGluXoIoxKSZy76fcVgMvk5jC4meBaOP2GfMJAI7Obtdzpa1Fa1qZCBtZa+OAYV8vlc8dKPOhUNw== native-watchdog@1.3.0: version "1.3.0" From a6b1892f14c7a74be84202362bcd927175bec75b Mon Sep 17 00:00:00 2001 From: annkamsk Date: Thu, 27 Aug 2020 17:02:19 +0200 Subject: [PATCH 601/736] Create HTML with dom.ts#$ function calls instead of string concatenation --- src/vs/base/browser/dom.ts | 12 ++++ .../browser/inspectTokens/inspectTokens.ts | 65 ++++++++++--------- 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 29490d6fc1e..0e254009357 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -1009,6 +1009,18 @@ export function prepend(parent: HTMLElement, child: T): T { const SELECTOR_REGEX = /([\w\-]+)?(#([\w\-]+))?((\.([\w\-]+))*)/; +export function reset(parent: HTMLElement, ...children: Array) { + parent.innerText = ''; + coalesce(children) + .forEach(child => { + if (child instanceof Node) { + parent.appendChild(child); + } else { + parent.appendChild(document.createTextNode(child as string)); + } + }); +} + export enum Namespace { HTML = 'http://www.w3.org/1999/xhtml', SVG = 'http://www.w3.org/2000/svg' diff --git a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts index f219c6cafec..31108f99ddb 100644 --- a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts +++ b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./inspectTokens'; +import { $, append, reset } from 'vs/base/browser/dom'; import { CharCode } from 'vs/base/common/charCode'; import { Color } from 'vs/base/common/color'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; -import { escape } from 'vs/base/common/strings'; import { ContentWidgetPositionPreference, IActiveCodeEditor, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; @@ -115,23 +115,11 @@ function renderTokenText(tokenText: string): string { let charCode = tokenText.charCodeAt(charIndex); switch (charCode) { case CharCode.Tab: - result += '→'; + result += '\u2192'; // → break; case CharCode.Space: - result += '·'; - break; - - case CharCode.LessThan: - result += '<'; - break; - - case CharCode.GreaterThan: - result += '>'; - break; - - case CharCode.Ampersand: - result += '&'; + result += '\u00B7'; // · break; default: @@ -211,8 +199,6 @@ class InspectTokensWidget extends Disposable implements IContentWidget { } } - let result = ''; - let lineContent = this._model.getLineContent(position.lineNumber); let tokenText = ''; if (token1Index < data.tokens1.length) { @@ -220,26 +206,43 @@ class InspectTokensWidget extends Disposable implements IContentWidget { let tokenEndIndex = token1Index + 1 < data.tokens1.length ? data.tokens1[token1Index + 1].offset : lineContent.length; tokenText = lineContent.substring(tokenStartIndex, tokenEndIndex); } - result += `

    ${renderTokenText(tokenText)}(${tokenText.length} ${tokenText.length === 1 ? 'char' : 'chars'})

    `; + reset(this._domNode, + $('h2.tm-token', undefined, renderTokenText(tokenText), + $('span.tm-token-length', undefined, `${tokenText.length} ${tokenText.length === 1 ? 'char' : 'chars'}`))); - result += `
    `; + append(this._domNode, $('hr.tokens-inspect-separator', { 'style': 'clear:both' })); - let metadata = (token2Index << 1) + 1 < data.tokens2.length ? this._decodeMetadata(data.tokens2[(token2Index << 1) + 1]) : null; - result += ``; - result += ``; - result += ``; - result += ``; - result += ``; - result += ``; - result += ``; - - result += `
    `; + const metadata = (token2Index << 1) + 1 < data.tokens2.length ? this._decodeMetadata(data.tokens2[(token2Index << 1) + 1]) : null; + append(this._domNode, $('table.tm-metadata-table', undefined, + $('tbody', undefined, + $('tr', undefined, + $('td.tm-metadata-key', undefined, 'language'), + $('td.tm-metadata-value', undefined, `${metadata ? metadata.languageIdentifier.language : '-?-'}`) + ), + $('tr', undefined, + $('td.tm-metadata-key', undefined, 'token type' as string), + $('td.tm-metadata-value', undefined, `${metadata ? this._tokenTypeToString(metadata.tokenType) : '-?-'}`) + ), + $('tr', undefined, + $('td.tm-metadata-key', undefined, 'font style' as string), + $('td.tm-metadata-value', undefined, `${metadata ? this._fontStyleToString(metadata.fontStyle) : '-?-'}`) + ), + $('tr', undefined, + $('td.tm-metadata-key', undefined, 'foreground'), + $('td.tm-metadata-value', undefined, `${metadata ? Color.Format.CSS.formatHex(metadata.foreground) : '-?-'}`) + ), + $('tr', undefined, + $('td.tm-metadata-key', undefined, 'background'), + $('td.tm-metadata-value', undefined, `${metadata ? Color.Format.CSS.formatHex(metadata.background) : '-?-'}`) + ) + ) + )); + append(this._domNode, $('hr.tokens-inspect-separator')); if (token1Index < data.tokens1.length) { - result += `${escape(data.tokens1[token1Index].type)}`; + append(this._domNode, $('span.tm-token-type', undefined, data.tokens1[token1Index].type)); } - this._domNode.innerHTML = result; this._editor.layoutContentWidget(this); } From 3c80a82a8eecf1350f70936d841032f94db163ca Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 27 Aug 2020 17:08:36 +0200 Subject: [PATCH 602/736] perf - load semver-umd only when needed in cli-process --- src/vs/code/node/cliProcessMain.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 9b06d39b0bc..b528f7dd343 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -7,7 +7,6 @@ import { localize } from 'vs/nls'; import { raceTimeout } from 'vs/base/common/async'; import product from 'vs/platform/product/common/product'; import * as path from 'vs/base/common/path'; -import * as semver from 'semver-umd'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -214,6 +213,8 @@ export class Main { throw new Error('Invalid vsix'); } + const semver = await import('semver-umd'); + const extensionIdentifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) }; const installedExtensions = await this.extensionManagementService.getInstalled(ExtensionType.User); const newer = installedExtensions.find(local => areSameExtensions(extensionIdentifier, local.identifier) && semver.gt(local.manifest.version, manifest.version)); From ef50db9f5937d4253bcf422bf992b29e1633955b Mon Sep 17 00:00:00 2001 From: n-gist Date: Thu, 27 Aug 2020 21:25:00 +0600 Subject: [PATCH 603/736] Requested changes --- .../contrib/snippet/snippetController2.ts | 4 +-- .../editor/contrib/snippet/snippetSession.ts | 6 ++--- .../contrib/snippet/snippetVariables.ts | 2 +- .../snippet/test/snippetSession.test.ts | 4 +-- .../snippet/test/snippetVariables.test.ts | 10 +++---- .../contrib/suggest/suggestController.ts | 2 +- .../suggest/suggestOvertypingCapturer.ts | 27 ++++++++++--------- 7 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/vs/editor/contrib/snippet/snippetController2.ts b/src/vs/editor/contrib/snippet/snippetController2.ts index 5c6abcb852a..7c35372a614 100644 --- a/src/vs/editor/contrib/snippet/snippetController2.ts +++ b/src/vs/editor/contrib/snippet/snippetController2.ts @@ -27,7 +27,7 @@ export interface ISnippetInsertOptions { undoStopBefore: boolean; undoStopAfter: boolean; clipboardText: string | undefined; - overtypingCapturer: OvertypingCapturer | null; + overtypingCapturer: OvertypingCapturer | undefined; } const _defaultOptions: ISnippetInsertOptions = { @@ -37,7 +37,7 @@ const _defaultOptions: ISnippetInsertOptions = { undoStopAfter: true, adjustWhitespace: true, clipboardText: undefined, - overtypingCapturer: null + overtypingCapturer: undefined }; export class SnippetController2 implements IEditorContribution { diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index d99b6ba9387..7f91f835c4c 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -320,7 +320,7 @@ export interface ISnippetSessionInsertOptions { overwriteAfter: number; adjustWhitespace: boolean; clipboardText: string | undefined; - overtypingCapturer: OvertypingCapturer | null; + overtypingCapturer: OvertypingCapturer | undefined; } const _defaultOptions: ISnippetSessionInsertOptions = { @@ -328,7 +328,7 @@ const _defaultOptions: ISnippetSessionInsertOptions = { overwriteAfter: 0, adjustWhitespace: true, clipboardText: undefined, - overtypingCapturer: null + overtypingCapturer: undefined }; export class SnippetSession { @@ -385,7 +385,7 @@ export class SnippetSession { return selection; } - static createEditsAndSnippets(editor: IActiveCodeEditor, template: string, overwriteBefore: number, overwriteAfter: number, enforceFinalTabstop: boolean, adjustWhitespace: boolean, clipboardText: string | undefined, overtypingCapturer: OvertypingCapturer | null): { edits: IIdentifiedSingleEditOperation[], snippets: OneSnippet[] } { + static createEditsAndSnippets(editor: IActiveCodeEditor, template: string, overwriteBefore: number, overwriteAfter: number, enforceFinalTabstop: boolean, adjustWhitespace: boolean, clipboardText: string | undefined, overtypingCapturer: OvertypingCapturer | undefined): { edits: IIdentifiedSingleEditOperation[], snippets: OneSnippet[] } { const edits: IIdentifiedSingleEditOperation[] = []; const snippets: OneSnippet[] = []; diff --git a/src/vs/editor/contrib/snippet/snippetVariables.ts b/src/vs/editor/contrib/snippet/snippetVariables.ts index fd87983cfe4..98381d6093c 100644 --- a/src/vs/editor/contrib/snippet/snippetVariables.ts +++ b/src/vs/editor/contrib/snippet/snippetVariables.ts @@ -74,7 +74,7 @@ export class SelectionBasedVariableResolver implements VariableResolver { private readonly _model: ITextModel, private readonly _selection: Selection, private readonly _selectionIdx: number, - private readonly _overtypingCapturer: OvertypingCapturer | null + private readonly _overtypingCapturer: OvertypingCapturer | undefined ) { // } diff --git a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts b/src/vs/editor/contrib/snippet/test/snippetSession.test.ts index db68a64643e..f72727ccdce 100644 --- a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetSession.test.ts @@ -127,7 +127,7 @@ suite('SnippetSession', function () { test('snippets, newline NO whitespace adjust', () => { editor.setSelection(new Selection(2, 5, 2, 5)); - const session = new SnippetSession(editor, 'abc\n foo\n bar\n$0', { overwriteBefore: 0, overwriteAfter: 0, adjustWhitespace: false, clipboardText: undefined, overtypingCapturer: null }); + const session = new SnippetSession(editor, 'abc\n foo\n bar\n$0', { overwriteBefore: 0, overwriteAfter: 0, adjustWhitespace: false, clipboardText: undefined, overtypingCapturer: undefined }); session.insert(); assert.equal(editor.getModel()!.getValue(), 'function foo() {\n abc\n foo\n bar\nconsole.log(a);\n}'); }); @@ -649,7 +649,7 @@ suite('SnippetSession', function () { assert.ok(actual.equalsSelection(new Selection(1, 9, 1, 12))); editor.setSelections([new Selection(1, 9, 1, 12)]); - new SnippetSession(editor, 'far', { overwriteBefore: 3, overwriteAfter: 0, adjustWhitespace: true, clipboardText: undefined, overtypingCapturer: null }).insert(); + new SnippetSession(editor, 'far', { overwriteBefore: 3, overwriteAfter: 0, adjustWhitespace: true, clipboardText: undefined, overtypingCapturer: undefined }).insert(); assert.equal(model.getValue(), 'console.far'); }); }); diff --git a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts index 89b2db3dbef..658039f8065 100644 --- a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts @@ -34,7 +34,7 @@ suite('Snippet Variables Resolver', function () { resolver = new CompositeSnippetVariableResolver([ new ModelBasedVariableResolver(labelService, model), - new SelectionBasedVariableResolver(model, new Selection(1, 1, 1, 1), 0, null), + new SelectionBasedVariableResolver(model, new Selection(1, 1, 1, 1), 0, undefined), ]); }); @@ -102,24 +102,24 @@ suite('Snippet Variables Resolver', function () { test('editor variables, selection', function () { - resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 2, 3), 0, null); + resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 2, 3), 0, undefined); assertVariableResolve(resolver, 'TM_SELECTED_TEXT', 'his is line one\nth'); assertVariableResolve(resolver, 'TM_CURRENT_LINE', 'this is line two'); assertVariableResolve(resolver, 'TM_LINE_INDEX', '1'); assertVariableResolve(resolver, 'TM_LINE_NUMBER', '2'); - resolver = new SelectionBasedVariableResolver(model, new Selection(2, 3, 1, 2), 0, null); + resolver = new SelectionBasedVariableResolver(model, new Selection(2, 3, 1, 2), 0, undefined); assertVariableResolve(resolver, 'TM_SELECTED_TEXT', 'his is line one\nth'); assertVariableResolve(resolver, 'TM_CURRENT_LINE', 'this is line one'); assertVariableResolve(resolver, 'TM_LINE_INDEX', '0'); assertVariableResolve(resolver, 'TM_LINE_NUMBER', '1'); - resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 1, 2), 0, null); + resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 1, 2), 0, undefined); assertVariableResolve(resolver, 'TM_SELECTED_TEXT', undefined); assertVariableResolve(resolver, 'TM_CURRENT_WORD', 'this'); - resolver = new SelectionBasedVariableResolver(model, new Selection(3, 1, 3, 1), 0, null); + resolver = new SelectionBasedVariableResolver(model, new Selection(3, 1, 3, 1), 0, undefined); assertVariableResolve(resolver, 'TM_CURRENT_WORD', undefined); }); diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index 0c24f87f89d..3b92a21d7cc 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -207,7 +207,7 @@ export class SuggestController implements IEditorContribution { // Wire up text overtyping capture this._overtypingCapturer = this._toDispose.add(new IdleValue(() => { - return this._toDispose.add(new OvertypingCapturer(this.editor, this.widget.value)); + return this._toDispose.add(new OvertypingCapturer(this.editor, this.model)); })); this._alternatives = this._toDispose.add(new IdleValue(() => { diff --git a/src/vs/editor/contrib/suggest/suggestOvertypingCapturer.ts b/src/vs/editor/contrib/suggest/suggestOvertypingCapturer.ts index 36ffbf946a4..e6231a7a95d 100644 --- a/src/vs/editor/contrib/suggest/suggestOvertypingCapturer.ts +++ b/src/vs/editor/contrib/suggest/suggestOvertypingCapturer.ts @@ -5,27 +5,27 @@ import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { SuggestWidget } from './suggestWidget'; +import { SuggestModel } from 'vs/editor/contrib/suggest/suggestModel'; export class OvertypingCapturer implements IDisposable { - private readonly _maxSelectionLength = 1000000; + private readonly _maxSelectionLength = 51200; private readonly _disposables = new DisposableStore(); - private readonly _editor: ICodeEditor; private _lastOvertyped: string[]; private _holdCurrent: boolean; + private _empty: boolean; - constructor(editor: ICodeEditor, widget: SuggestWidget) { - this._editor = editor; + constructor(editor: ICodeEditor, suggestModel: SuggestModel) { this._lastOvertyped = new Array(0); this._holdCurrent = false; + this._empty = true; - this._disposables.add(this._editor.onWillType(text => { + this._disposables.add(editor.onWillType(text => { if (this._holdCurrent) { return; } - const selections = this._editor.getSelections(); + const selections = editor.getSelections(); if (!selections) { return; } @@ -44,7 +44,7 @@ export class OvertypingCapturer implements IDisposable { return; } - const model = this._editor.getModel(); + const model = editor.getModel(); if (!model) { return; } @@ -60,22 +60,23 @@ export class OvertypingCapturer implements IDisposable { for (let i = 0; i < selectionsLength; i++) { this._lastOvertyped[i] = model.getValueInRange(selections[i]); } + this._empty = false; })); - this._disposables.add(widget.onDidShow(() => { + this._disposables.add(suggestModel.onDidSuggest(e => { this._holdCurrent = true; })); - this._disposables.add(widget.onDidHide(() => { - if (this._lastOvertyped.length > 0) { - this._lastOvertyped = new Array(0); + this._disposables.add(suggestModel.onDidCancel(e => { + if (!this._empty) { + this._empty = true; } this._holdCurrent = false; })); } getLastOvertypedText(idx: number): string | undefined { - if (idx < this._lastOvertyped.length && this._lastOvertyped[idx].length > 0) { + if (!this._empty && idx >= 0 && idx < this._lastOvertyped.length) { return this._lastOvertyped[idx]; } return undefined; From 450c37e2f7909ee7478d4999230143c13728275e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 27 Aug 2020 17:48:21 +0200 Subject: [PATCH 604/736] also send onNotebook:XYZ event, https://github.com/microsoft/vscode/issues/105496 --- .../workbench/contrib/notebook/browser/notebookServiceImpl.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index b994093ec72..bff66c05e13 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -538,6 +538,9 @@ export class NotebookService extends Disposable implements INotebookService, ICu // notebook providers/kernels/renderers might use `*` as activation event. await this._extensionService.activateByEvent(`*`); // this awaits full activation of all matching extensions + await this._extensionService.activateByEvent(`onNotebook:${viewType}`); + + // TODO@jrieken deprecated, remove this await this._extensionService.activateByEvent(`onNotebookEditor:${viewType}`); } return this._notebookProviders.has(viewType); From cc96f8ea2355f9bd48a40e6807eca67f19fec60b Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 27 Aug 2020 08:58:47 -0700 Subject: [PATCH 605/736] debug: bump js-debug-companion --- product.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product.json b/product.json index 3b18619d1fe..9e975d59c1b 100644 --- a/product.json +++ b/product.json @@ -76,7 +76,7 @@ }, { "name": "ms-vscode.js-debug-companion", - "version": "1.0.5", + "version": "1.0.6", "repo": "https://github.com/microsoft/vscode-js-debug-companion", "metadata": { "id": "99cb0b7f-7354-4278-b8da-6cc79972169d", From 8877f5f60abbf7b290c89b609ad259a533b8510f Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Thu, 27 Aug 2020 10:21:39 -0700 Subject: [PATCH 606/736] fixes #105480 --- src/vs/workbench/browser/layout.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index dd1e2146344..7a0533fd76f 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1539,6 +1539,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi setPanelHidden(hidden: boolean, skipLayout?: boolean): void { this.state.panel.hidden = hidden; + // Return if not initialized fully #105480 + if (!this.workbenchGrid) { + return; + } + // Adjust CSS if (hidden) { addClass(this.container, Classes.PANEL_HIDDEN); From fc1b768258e48d2d955f0f7cac67205dcc11596f Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Thu, 27 Aug 2020 10:25:27 -0700 Subject: [PATCH 607/736] fixes #90836 --- src/vs/workbench/browser/layout.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 7a0533fd76f..58e2817e7e0 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1637,7 +1637,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.state.menuBar.visibility = visibility; // Layout - if (!skipLayout) { + if (!skipLayout && this.workbenchGrid) { this.workbenchGrid.setViewVisible(this.titleBarPartView, this.isVisible(Parts.TITLEBAR_PART)); } } From 342899271ddf12f0f095bbab27ee3c98b6654b2a Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 27 Aug 2020 08:40:49 -0700 Subject: [PATCH 608/736] re #105346. --- .../workbench/contrib/notebook/browser/notebookEditorWidget.ts | 2 +- .../contrib/notebook/browser/view/renderers/cellRenderer.ts | 2 +- .../contrib/notebook/browser/view/renderers/markdownCell.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 7bdc292f3bf..d113667a8d1 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -441,7 +441,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor multipleSelectionSupport: false, enableKeyboardNavigation: true, additionalScrollHeight: 0, - transformOptimization: (isMacintosh && isNative) || getTitleBarStyle(this.configurationService, this.environmentService) === 'native', + transformOptimization: false, //(isMacintosh && isNative) || getTitleBarStyle(this.configurationService, this.environmentService) === 'native', styleController: (_suffix: string) => { return this._list!; }, overrideStyles: { listBackground: editorBackground, diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index eb5c7a65cb1..d5aeb2571af 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -660,7 +660,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende width: 0, height: 0 }, - overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode() + // overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode() }, {}); disposables.add(this.editorOptions.onDidChange(newValue => editor.updateOptions(newValue))); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts index a1e81ae72e5..a0fea876cd3 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts @@ -190,7 +190,7 @@ export class StatefulMarkdownCell extends Disposable { width: width, height: editorHeight }, - overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode() + // overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode() }, {}); this.templateData.currentEditor = this.editor; From 883749806b2b7e3494e5dd148ddb577857463d87 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 27 Aug 2020 11:25:25 -0700 Subject: [PATCH 609/736] support revert notebook cell metadata in diff view. --- extensions/vscode-notebook-tests/package.json | 8 +- src/vs/platform/actions/common/actions.ts | 2 + src/vs/vscode.proposed.d.ts | 2 +- .../notebook/browser/diff/cellComponents.ts | 246 ++++++++++++++---- .../notebook/browser/diff/notebookDiff.css | 12 + .../browser/diff/notebookDiffActions.ts | 31 +++ .../notebook/browser/notebook.contribution.ts | 72 +++++ .../contrib/notebook/common/notebookCommon.ts | 9 + 8 files changed, 326 insertions(+), 56 deletions(-) diff --git a/extensions/vscode-notebook-tests/package.json b/extensions/vscode-notebook-tests/package.json index 53ba12569a9..4454b25fb2a 100644 --- a/extensions/vscode-notebook-tests/package.json +++ b/extensions/vscode-notebook-tests/package.json @@ -78,6 +78,12 @@ "group": "inline@1" } ] - } + }, + "jsonValidation": [ + { + "fileMatch": "vscode://vscode-notebook-cell-metadata/*", + "url": "vscode://schemas/notebook/cellmetadata" + } + ] } } diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 687cefd9ef1..94d8bff6c76 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -123,6 +123,8 @@ export class MenuId { static readonly NotebookCellInsert = new MenuId('NotebookCellInsert'); static readonly NotebookCellBetween = new MenuId('NotebookCellBetween'); static readonly NotebookCellListTop = new MenuId('NotebookCellTop'); + static readonly NotebookDiffCellMetadataTitle = new MenuId('NotebookDiffCellMetadataTitle'); + static readonly NotebookDiffCellOutputsTitle = new MenuId('NotebookDiffCellOutputsTitle'); static readonly BulkEditTitle = new MenuId('BulkEditTitle'); static readonly BulkEditContext = new MenuId('BulkEditContext'); static readonly TimelineItemContext = new MenuId('TimelineItemContext'); diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index c898362a2e4..cf7cd17fb93 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1179,7 +1179,7 @@ declare module 'vscode' { export interface NotebookCellMetadata { /** - * Controls if the content of a cell is editable or not. + * Controls whether a cell's editor is editable/readonly. */ editable?: boolean; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index 868c3ab9a8e..d5df0762d40 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -17,8 +17,17 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { format } from 'vs/base/common/jsonFormatter'; import { applyEdits } from 'vs/base/common/jsonEdit'; -import { NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellUri, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { hash } from 'vs/base/common/hash'; +import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IMenu, IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; +import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IAction } from 'vs/base/common/actions'; +import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; const fixedEditorOptions: IEditorOptions = { padding: { @@ -61,6 +70,8 @@ const fixedDiffEditorOptions: IDiffEditorOptions = { class PropertyHeader extends Disposable { protected _foldingIndicator!: HTMLElement; protected _statusSpan!: HTMLElement; + protected _toolbar!: ToolBar; + protected _menu!: IMenu; constructor( readonly cell: CellDiffViewModel, @@ -74,7 +85,13 @@ class PropertyHeader extends Disposable { unChangedLabel: string; changedLabel: string; prefix: string; - } + menuId: MenuId; + }, + @IContextMenuService readonly contextMenuService: IContextMenuService, + @IKeybindingService readonly keybindingService: IKeybindingService, + @INotificationService readonly notificationService: INotificationService, + @IMenuService readonly menuService: IMenuService, + @IContextKeyService readonly contextKeyService: IContextKeyService ) { super(); } @@ -83,8 +100,6 @@ class PropertyHeader extends Disposable { let metadataChanged = this.accessor.checkIfModified(this.cell); this._foldingIndicator = DOM.append(this.metadataHeaderContainer, DOM.$('.property-folding-indicator')); DOM.addClass(this._foldingIndicator, this.accessor.prefix); - - this._updateFoldingIcon(); const metadataStatus = DOM.append(this.metadataHeaderContainer, DOM.$('div.property-status')); this._statusSpan = DOM.append(metadataStatus, DOM.$('span')); @@ -97,6 +112,29 @@ class PropertyHeader extends Disposable { this._statusSpan.textContent = this.accessor.unChangedLabel; } + const cellToolbarContainer = DOM.append(this.metadataHeaderContainer, DOM.$('div.property-toolbar')); + this._toolbar = new ToolBar(cellToolbarContainer, this.contextMenuService, { + actionViewItemProvider: action => { + if (action instanceof MenuItemAction) { + const item = new CodiconActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService); + return item; + } + + return undefined; + } + }); + this._toolbar.context = { + cell: this.cell + }; + + this._menu = this.menuService.createMenu(this.accessor.menuId, this.contextKeyService); + + if (metadataChanged) { + const actions: IAction[] = []; + createAndFillInActionBarActions(this._menu, { shouldForwardArgs: true }, actions); + this._toolbar.setActions(actions); + } + this._register(this.notebookEditor.onMouseUp(e => { if (!e.event.target) { return; @@ -138,6 +176,22 @@ class PropertyHeader extends Disposable { this.accessor.updateInfoRendering(); } + refresh() { + let metadataChanged = this.accessor.checkIfModified(this.cell); + if (metadataChanged) { + this._statusSpan.textContent = this.accessor.changedLabel; + this._statusSpan.style.fontWeight = 'bold'; + DOM.addClass(this.metadataHeaderContainer, 'modified'); + const actions: IAction[] = []; + createAndFillInActionBarActions(this._menu, undefined, actions); + this._toolbar.setActions(actions); + } else { + this._statusSpan.textContent = this.accessor.unChangedLabel; + this._statusSpan.style.fontWeight = 'normal'; + this._toolbar.setActions([]); + } + } + private _updateFoldingIcon() { if (this.accessor.getFoldingState(this.cell) === PropertyFoldingState.Collapsed) { this._foldingIndicator.innerHTML = renderCodicons('$(chevron-right)'); @@ -229,14 +283,15 @@ abstract class AbstractCellRenderer extends Disposable { this._metadataHeaderContainer = DOM.append(this._diffEditorContainer, DOM.$('.metadata-header-container')); this._metadataInfoContainer = DOM.append(this._diffEditorContainer, DOM.$('.metadata-info-container')); - this._metadataHeader = new PropertyHeader( + this._metadataHeader = this.instantiationService.createInstance( + PropertyHeader, this.cell, this._metadataHeaderContainer, this.notebookEditor, { updateInfoRendering: this.updateMetadataRendering.bind(this), checkIfModified: (cell) => { - return cell.type === 'modified' && hash(this._getFormatedMetadataJSON(cell.original?.metadata || {})) !== hash(this._getFormatedMetadataJSON(cell.modified?.metadata ?? {})); + return hash(this._getFormatedMetadataJSON(cell.original?.metadata || {}, cell.original?.language)) !== hash(this._getFormatedMetadataJSON(cell.modified?.metadata ?? {}, cell.modified?.language)); }, getFoldingState: (cell) => { return cell.metadataFoldingState; @@ -246,7 +301,8 @@ abstract class AbstractCellRenderer extends Disposable { }, unChangedLabel: 'Metadata', changedLabel: 'Metadata changed', - prefix: 'metadata' + prefix: 'metadata', + menuId: MenuId.NotebookDiffCellMetadataTitle } ); this._register(this._metadataHeader); @@ -255,7 +311,8 @@ abstract class AbstractCellRenderer extends Disposable { this._outputHeaderContainer = DOM.append(this._diffEditorContainer, DOM.$('.output-header-container')); this._outputInfoContainer = DOM.append(this._diffEditorContainer, DOM.$('.output-info-container')); - this._outputHeader = new PropertyHeader( + this._outputHeader = this.instantiationService.createInstance( + PropertyHeader, this.cell, this._outputHeaderContainer, this.notebookEditor, @@ -272,7 +329,8 @@ abstract class AbstractCellRenderer extends Disposable { }, unChangedLabel: 'Outputs', changedLabel: 'Outputs changed', - prefix: 'output' + prefix: 'output', + menuId: MenuId.NotebookDiffCellOutputsTitle } ); this._register(this._outputHeader); @@ -310,7 +368,6 @@ abstract class AbstractCellRenderer extends Disposable { this._outputEditorContainer = DOM.append(this._outputInfoContainer, DOM.$('.output-editor-container')); this._buildOutputEditor(); } else { - console.log(this.cell); this._layoutInfo.outputHeight = this._outputEditor.getContentHeight(); this.layout({ outputEditor: true }); } @@ -323,21 +380,7 @@ abstract class AbstractCellRenderer extends Disposable { } protected _getFormatedMetadataJSON(metadata: NotebookCellMetadata, language?: string) { - let filteredMetadata: { [key: string]: any } = {}; - if (this.notebookEditor.textModel) { - const transientMetadata = this.notebookEditor.textModel!.transientOptions.transientMetadata; - - const keys = new Set([...Object.keys(metadata)]); - for (let key of keys) { - if (!(transientMetadata[key as keyof NotebookCellMetadata]) - ) { - filteredMetadata[key] = metadata[key as keyof NotebookCellMetadata]; - } - } - } else { - filteredMetadata = metadata; - } - + const filteredMetadata: { [key: string]: any } = metadata; const content = JSON.stringify({ language, ...filteredMetadata @@ -349,39 +392,124 @@ abstract class AbstractCellRenderer extends Disposable { return metadataSource; } + private _applySanitizedMetadataChanges(currentMetadata: NotebookCellMetadata, newMetadata: any) { + let result: { [key: string]: any } = {}; + let newLangauge: string | undefined = undefined; + try { + const newMetadataObj = JSON.parse(newMetadata); + const keys = new Set([...Object.keys(newMetadataObj)]); + for (let key of keys) { + switch (key as keyof NotebookCellMetadata) { + case 'breakpointMargin': + case 'editable': + case 'hasExecutionOrder': + case 'inputCollapsed': + case 'outputCollapsed': + case 'runnable': + // boolean + if (typeof newMetadataObj[key] === 'boolean') { + result[key] = newMetadataObj[key]; + } else { + result[key] = currentMetadata[key as keyof NotebookCellMetadata]; + } + break; + + case 'executionOrder': + case 'lastRunDuration': + // number + if (typeof newMetadataObj[key] === 'number') { + result[key] = newMetadataObj[key]; + } else { + result[key] = currentMetadata[key as keyof NotebookCellMetadata]; + } + break; + case 'runState': + // enum + if (typeof newMetadataObj[key] === 'number' && [1, 2, 3, 4].indexOf(newMetadataObj[key]) >= 0) { + result[key] = newMetadataObj[key]; + } else { + result[key] = currentMetadata[key as keyof NotebookCellMetadata]; + } + break; + case 'statusMessage': + // string + if (typeof newMetadataObj[key] === 'string') { + result[key] = newMetadataObj[key]; + } else { + result[key] = currentMetadata[key as keyof NotebookCellMetadata]; + } + break; + default: + if (key === 'language') { + newLangauge = newMetadataObj[key]; + } + result[key] = newMetadataObj[key]; + break; + } + } + + if (newLangauge !== undefined && newLangauge !== this.cell.modified!.language) { + this.notebookEditor.textModel!.changeCellLanguage(this.cell.modified!.handle, newLangauge); + } + this.notebookEditor.textModel!.changeCellMetadata(this.cell.modified!.handle, result, false); + } catch { + } + } + private _buildMetadataEditor() { - if (this.cell.type === 'modified') { + if (this.cell.type === 'modified' || this.cell.type === 'unchanged') { const originalMetadataSource = this._getFormatedMetadataJSON(this.cell.original?.metadata || {}, this.cell.original?.language); const modifiedMetadataSource = this._getFormatedMetadataJSON(this.cell.modified?.metadata || {}, this.cell.modified?.language); - if (originalMetadataSource !== modifiedMetadataSource) { - this._metadataEditor = this.instantiationService.createInstance(DiffEditorWidget, this._metadataEditorContainer!, { - ...fixedDiffEditorOptions, - overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode(), - readOnly: true - }); + this._metadataEditor = this.instantiationService.createInstance(DiffEditorWidget, this._metadataEditorContainer!, { + ...fixedDiffEditorOptions, + overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode(), + readOnly: false, + originalEditable: false + }); - DOM.addClass(this._metadataEditorContainer!, 'diff'); + DOM.addClass(this._metadataEditorContainer!, 'diff'); - const mode = this.modeService.create('json'); - const originalMetadataModel = this.modelService.createModel(originalMetadataSource, mode, undefined, true); - const modifiedMetadataModel = this.modelService.createModel(modifiedMetadataSource, mode, undefined, true); - this._metadataEditor.setModel({ - original: originalMetadataModel, - modified: modifiedMetadataModel - }); + const mode = this.modeService.create('json'); + const originalMetadataModel = this.modelService.createModel(originalMetadataSource, mode, CellUri.generateCellMetadataUri(this.cell.original!.uri, this.cell.original!.handle), false); + const modifiedMetadataModel = this.modelService.createModel(modifiedMetadataSource, mode, CellUri.generateCellMetadataUri(this.cell.modified!.uri, this.cell.modified!.handle), false); + this._metadataEditor.setModel({ + original: originalMetadataModel, + modified: modifiedMetadataModel + }); - this._layoutInfo.metadataHeight = this._metadataEditor.getContentHeight(); - this.layout({ metadataEditor: true }); + this._register(originalMetadataModel); + this._register(modifiedMetadataModel); - this._register(this._metadataEditor.onDidContentSizeChange((e) => { - if (e.contentHeightChanged && this.cell.metadataFoldingState === PropertyFoldingState.Expanded) { - this._layoutInfo.metadataHeight = e.contentHeight; - this.layout({ metadataEditor: true }); - } - })); + this._layoutInfo.metadataHeight = this._metadataEditor.getContentHeight(); + this.layout({ metadataEditor: true }); - return; - } + this._register(this._metadataEditor.onDidContentSizeChange((e) => { + if (e.contentHeightChanged && this.cell.metadataFoldingState === PropertyFoldingState.Expanded) { + this._layoutInfo.metadataHeight = e.contentHeight; + this.layout({ metadataEditor: true }); + } + })); + + let respondingToContentChange = false; + + this._register(modifiedMetadataModel.onDidChangeContent(() => { + respondingToContentChange = true; + const value = modifiedMetadataModel.getValue(); + this._applySanitizedMetadataChanges(this.cell.modified!.metadata, value); + this._metadataHeader.refresh(); + respondingToContentChange = false; + })); + + this._register(this.cell.modified!.onDidChangeMetadata(() => { + if (respondingToContentChange) { + return; + } + + const modifiedMetadataSource = this._getFormatedMetadataJSON(this.cell.modified?.metadata || {}, this.cell.modified?.language); + modifiedMetadataModel.setValue(modifiedMetadataSource); + })); + + return; } this._metadataEditor = this.instantiationService.createInstance(CodeEditorWidget, this._metadataEditorContainer!, { @@ -390,16 +518,26 @@ abstract class AbstractCellRenderer extends Disposable { width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true), height: 0 }, - overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode() + overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode(), + readOnly: false }, {}); - const mode = this.modeService.create('json'); + const mode = this.modeService.create('jsonc'); const originalMetadataSource = this._getFormatedMetadataJSON( this.cell.type === 'insert' ? this.cell.modified!.metadata || {} : this.cell.original!.metadata || {}); - const metadataModel = this.modelService.createModel(originalMetadataSource, mode, undefined, true); + const uri = this.cell.type === 'insert' + ? this.cell.modified!.uri + : this.cell.original!.uri; + const handle = this.cell.type === 'insert' + ? this.cell.modified!.handle + : this.cell.original!.handle; + + const modelUri = CellUri.generateCellMetadataUri(uri, handle); + const metadataModel = this.modelService.createModel(originalMetadataSource, mode, modelUri, false); this._metadataEditor.setModel(metadataModel); + this._register(metadataModel); this._layoutInfo.metadataHeight = this._metadataEditor.getContentHeight(); this.layout({ metadataEditor: true }); @@ -422,7 +560,7 @@ abstract class AbstractCellRenderer extends Disposable { } private _buildOutputEditor() { - if (this.cell.type === 'modified' && !this.notebookEditor.textModel!.transientOptions.transientOutputs) { + if ((this.cell.type === 'modified' || this.cell.type === 'unchanged') && !this.notebookEditor.textModel!.transientOptions.transientOutputs) { const originalOutputsSource = this._getFormatedOutputJSON(this.cell.original?.outputs || []); const modifiedOutputsSource = this._getFormatedOutputJSON(this.cell.modified?.outputs || []); if (originalOutputsSource !== modifiedOutputsSource) { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css index dda6833c788..113ad130dae 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css @@ -79,6 +79,18 @@ cursor: pointer; } +.notebook-text-diff-editor .cell-diff-editor-container .output-header-container, +.notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container { + display: flex; + flex-direction: row; + align-items: center; +} + +.notebook-text-diff-editor .cell-diff-editor-container .output-header-container .property-toolbar, +.notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .property-toolbar { + margin-left: auto; +} + .notebook-text-diff-editor .cell-diff-editor-container .output-header-container .property-status, .notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .property-status { font-size: 12px; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts index 2ffd15bb0c2..81d405e66d5 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts @@ -8,6 +8,7 @@ import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/act import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; import { ActiveEditorContext } from 'vs/workbench/common/editor'; +import { CellDiffViewModel } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; import { NotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor'; import { NotebookDiffEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookDiffEditorInput'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -48,3 +49,33 @@ registerAction2(class extends Action2 { } } }); + +registerAction2(class extends Action2 { + constructor() { + super( + { + id: 'notebook.diff.cell.revertMetadata', + title: localize('notebook.diff.cell.revertMetadata', "Revert Metadata"), + icon: { id: 'codicon/discard' }, + f1: false, + menu: { + id: MenuId.NotebookDiffCellMetadataTitle + } + } + ); + } + run(accessor: ServicesAccessor, context?: { cell: CellDiffViewModel }) { + if (!context) { + return; + } + + const original = context.cell.original; + const modified = context.cell.modified; + + if (!original || !modified) { + return; + } + + modified.metadata = original.metadata; + } +}); diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 74df84fbeb1..f4c192236af 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -48,6 +48,8 @@ import { INotebookEditorWorkerService } from 'vs/workbench/contrib/notebook/comm import { NotebookEditorWorkerServiceImpl } from 'vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; import { NotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/browser/notebookCellStatusBarServiceImpl'; +import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; // Editor Contribution @@ -440,9 +442,79 @@ class CellContentProvider implements ITextModelContentProvider { } } +class RegisterSchemasContribution extends Disposable implements IWorkbenchContribution { + constructor() { + super(); + this.registerMetadataSchemas(); + } + + private registerMetadataSchemas(): void { + const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); + const metadataSchema: IJSONSchema = { + properties: { + ['language']: { + type: 'string', + description: 'The language for the cell' + }, + ['editable']: { + type: 'boolean', + description: `Controls whether a cell's editor is editable/readonly` + }, + ['runnable']: { + type: 'boolean', + description: 'Controls if the cell is executable' + }, + ['breakpointMargin']: { + type: 'boolean', + description: 'Controls if the cell has a margin to support the breakpoint UI' + }, + ['hasExecutionOrder']: { + type: 'boolean', + description: 'Whether the execution order indicator will be displayed' + }, + ['executionOrder']: { + type: 'number', + description: 'The order in which this cell was executed' + }, + ['statusMessage']: { + type: 'string', + description: `A status message to be shown in the cell's status bar` + }, + ['runState']: { + type: 'integer', + description: `The cell's current run state` + }, + ['runStartTime']: { + type: 'number', + description: 'If the cell is running, the time at which the cell started running' + }, + ['lastRunDuration']: { + type: 'number', + description: `The total duration of the cell's last run` + }, + ['inputCollapsed']: { + type: 'boolean', + description: `Whether a code cell's editor is collapsed` + }, + ['outputCollapsed']: { + type: 'boolean', + description: `Whether a code cell's outputs are collapsed` + } + }, + // patternProperties: allSettings.patternProperties, + additionalProperties: true, + allowTrailingCommas: true, + allowComments: true + }; + + jsonRegistry.registerSchema('vscode://schemas/notebook/cellmetadata', metadataSchema); + } +} + const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchContributionsRegistry.registerWorkbenchContribution(NotebookContribution, LifecyclePhase.Starting); workbenchContributionsRegistry.registerWorkbenchContribution(CellContentProvider, LifecyclePhase.Starting); +workbenchContributionsRegistry.registerWorkbenchContribution(RegisterSchemasContribution, LifecyclePhase.Starting); registerSingleton(INotebookService, NotebookService); registerSingleton(INotebookEditorWorkerService, NotebookEditorWorkerServiceImpl); diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index c4e05bd2be4..42875da23ae 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -474,6 +474,7 @@ export function getCellUndoRedoComparisonKey(uri: URI) { export namespace CellUri { export const scheme = Schemas.vscodeNotebookCell; + const _regex = /^\d{7,}/; export function generate(notebook: URI, handle: number): URI { @@ -483,6 +484,14 @@ export namespace CellUri { }); } + export function generateCellMetadataUri(notebook: URI, handle: number): URI { + return notebook.with({ + scheme: Schemas.vscode, + authority: 'vscode-notebook-cell-metadata', + fragment: `${handle.toString().padStart(7, '0')}${notebook.scheme !== Schemas.file ? notebook.scheme : ''}` + }); + } + export function parse(cell: URI): { notebook: URI, handle: number } | undefined { if (cell.scheme !== scheme) { return undefined; From 20e9b0b78f3b6e3f58231d870812a51c77897024 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 27 Aug 2020 11:32:38 -0700 Subject: [PATCH 610/736] support reverting outputs. --- .../notebook/browser/diff/cellComponents.ts | 6 ++++ .../browser/diff/notebookDiffActions.ts | 30 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index d5df0762d40..d831793d8b5 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -590,6 +590,12 @@ abstract class AbstractCellRenderer extends Disposable { } })); + this._register(this.cell.modified!.onDidChangeOutputs(() => { + const modifiedOutputsSource = this._getFormatedOutputJSON(this.cell.modified?.outputs || []); + modifiedModel.setValue(modifiedOutputsSource); + this._outputHeader.refresh(); + })); + return; } } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts index 81d405e66d5..856b0aae9bf 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts @@ -79,3 +79,33 @@ registerAction2(class extends Action2 { modified.metadata = original.metadata; } }); + +registerAction2(class extends Action2 { + constructor() { + super( + { + id: 'notebook.diff.cell.revertOutputs', + title: localize('notebook.diff.cell.revertOutputs', "Revert Outputs"), + icon: { id: 'codicon/discard' }, + f1: false, + menu: { + id: MenuId.NotebookDiffCellOutputsTitle + } + } + ); + } + run(accessor: ServicesAccessor, context?: { cell: CellDiffViewModel }) { + if (!context) { + return; + } + + const original = context.cell.original; + const modified = context.cell.modified; + + if (!original || !modified) { + return; + } + + modified.spliceNotebookCellOutputs([[0, modified.outputs.length, original.outputs]]); + } +}); From 3bd7c6981e703542ce92d0e1fb8d3fa522cd3fdf Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 27 Aug 2020 11:38:13 -0700 Subject: [PATCH 611/736] enable enhanced text diff view for notebook by default. --- .../contrib/notebook/browser/notebook.contribution.ts | 9 +++++++-- .../workbench/contrib/notebook/common/notebookCommon.ts | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index f4c192236af..09954d65527 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -30,7 +30,7 @@ import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEd import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { NotebookService } from 'vs/workbench/contrib/notebook/browser/notebookServiceImpl'; -import { CellKind, CellToolbarLocKey, CellUri, DisplayOrderKey, getCellUndoRedoComparisonKey, NotebookDocumentBackupData, NotebookEditorPriority, ShowCellStatusbarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellToolbarLocKey, CellUri, DisplayOrderKey, getCellUndoRedoComparisonKey, NotebookDocumentBackupData, NotebookEditorPriority, NotebookTextDiffEditorPreview, ShowCellStatusbarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService, IOpenEditorOverride } from 'vs/workbench/services/editor/common/editorService'; @@ -249,7 +249,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri return undefined; } - if (originalInput instanceof DiffEditorInput && this.configurationService.getValue('notebook.diff.enablePreview')) { + if (originalInput instanceof DiffEditorInput && this.configurationService.getValue(NotebookTextDiffEditorPreview)) { return this._handleDiffEditorInput(originalInput, options, group); } @@ -546,6 +546,11 @@ configurationRegistry.registerConfiguration({ description: nls.localize('notebook.showCellStatusbar.description', "Whether the cell statusbar should be shown."), type: 'boolean', default: true + }, + [NotebookTextDiffEditorPreview]: { + description: nls.localize('notebook.diff.enablePreview.description', "Whether to use the enhanced text diff editor for notebook."), + type: 'boolean', + default: true } } }); diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 42875da23ae..8f3e2d757d9 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -789,6 +789,7 @@ export interface INotebookCellStatusBarEntry { export const DisplayOrderKey = 'notebook.displayOrder'; export const CellToolbarLocKey = 'notebook.cellToolbarLocation'; export const ShowCellStatusbarKey = 'notebook.showCellStatusbar'; +export const NotebookTextDiffEditorPreview = 'notebook.diff.enablePreview'; export const enum CellStatusbarAlignment { LEFT, From 650197b991775f5ef64a941637ba0aecf5660a0b Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Thu, 27 Aug 2020 11:50:51 -0700 Subject: [PATCH 612/736] Add authentication contribution point, #103507 --- extensions/github-authentication/package.json | 9 +- .../microsoft-authentication/package.json | 9 +- .../platform/extensions/common/extensions.ts | 6 ++ .../api/browser/mainThreadAuthentication.ts | 6 ++ .../workbench/api/common/extHost.protocol.ts | 1 + .../api/common/extHostAuthentication.ts | 5 ++ .../extensions/browser/extensionEditor.ts | 27 ++++++ .../browser/authenticationService.ts | 86 ++++++++++++++++++- 8 files changed, 143 insertions(+), 6 deletions(-) diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index 787b4d17497..b74a91843a3 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -17,7 +17,6 @@ "web" ], "activationEvents": [ - "*", "onAuthenticationRequest:github" ], "contributes": { @@ -34,7 +33,13 @@ "when": "false" } ] - } + }, + "authentication": [ + { + "label": "GitHub", + "id": "github" + } + ] }, "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", "main": "./out/extension.js", diff --git a/extensions/microsoft-authentication/package.json b/extensions/microsoft-authentication/package.json index 81ed5c32e4f..64becb50468 100644 --- a/extensions/microsoft-authentication/package.json +++ b/extensions/microsoft-authentication/package.json @@ -12,7 +12,6 @@ ], "enableProposedApi": true, "activationEvents": [ - "*", "onAuthenticationRequest:microsoft" ], "extensionKind": [ @@ -20,6 +19,14 @@ "workspace", "web" ], + "contributes": { + "authentication": [ + { + "label": "Microsoft", + "id": "microsoft" + } + ] + }, "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", "main": "./out/extension.js", "browser": "./dist/browser/extension.js", diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index 6024a71fb10..73c21c8824f 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -108,6 +108,11 @@ export interface ICodeActionContribution { readonly actions: readonly ICodeActionContributionAction[]; } +export interface IAuthenticationContribution { + readonly id: string; + readonly label: string; +} + export interface IExtensionContributions { commands?: ICommand[]; configuration?: IConfiguration | IConfiguration[]; @@ -126,6 +131,7 @@ export interface IExtensionContributions { localizations?: ILocalization[]; readonly customEditors?: readonly IWebviewEditor[]; readonly codeActions?: readonly ICodeActionContribution[]; + authentication?: IAuthenticationContribution[]; } export type ExtensionKind = 'ui' | 'workspace' | 'web'; diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts index fb2e5ddfeda..639c613ad67 100644 --- a/src/vs/workbench/api/browser/mainThreadAuthentication.ts +++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts @@ -232,6 +232,12 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu this._register(this.authenticationService.onDidUnregisterAuthenticationProvider(info => { this._proxy.$onDidChangeAuthenticationProviders([], [info]); })); + + this._proxy.$setProviders(this.authenticationService.declaredProviders); + + this._register(this.authenticationService.onDidChangeDeclaredProviders(e => { + this._proxy.$setProviders(e); + })); } $getProviderIds(): Promise { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 6b001f1df78..009620eadd1 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1055,6 +1055,7 @@ export interface ExtHostAuthenticationShape { $logout(id: string, sessionId: string): Promise; $onDidChangeAuthenticationSessions(id: string, label: string, event: modes.AuthenticationSessionsChangeEvent): Promise; $onDidChangeAuthenticationProviders(added: modes.AuthenticationProviderInformation[], removed: modes.AuthenticationProviderInformation[]): Promise; + $setProviders(providers: modes.AuthenticationProviderInformation[]): Promise; } export interface ExtHostSearchShape { diff --git a/src/vs/workbench/api/common/extHostAuthentication.ts b/src/vs/workbench/api/common/extHostAuthentication.ts index da8a6820ca0..c7dd0f2d5b2 100644 --- a/src/vs/workbench/api/common/extHostAuthentication.ts +++ b/src/vs/workbench/api/common/extHostAuthentication.ts @@ -28,6 +28,11 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape { this._proxy = mainContext.getProxy(MainContext.MainThreadAuthentication); } + $setProviders(providers: vscode.AuthenticationProviderInformation[]): Promise { + this._providers = providers; + return Promise.resolve(); + } + getProviderIds(): Promise> { return this._proxy.$getProviderIds(); } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 502a12a626a..b0198b8aab4 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -904,6 +904,7 @@ export class ExtensionEditor extends EditorPane { this.renderViews(content, manifest, layout), this.renderLocalizations(content, manifest, layout), this.renderCustomEditors(content, manifest, layout), + this.renderAuthentication(content, manifest, layout), ]; scrollableContent.scanDomNode(); @@ -1151,6 +1152,32 @@ export class ExtensionEditor extends EditorPane { return true; } + private renderAuthentication(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean { + const authentication = manifest.contributes?.authentication || []; + if (!authentication.length) { + return false; + } + + const details = $('details', { open: true, ontoggle: onDetailsToggle }, + $('summary', { tabindex: '0' }, localize('authentication', "Authentication ({0})", authentication.length)), + $('table', undefined, + $('tr', undefined, + $('th', undefined, localize('authentication.label', "Label")), + $('th', undefined, localize('authentication.id', "Id")) + ), + ...authentication.map(action => + $('tr', undefined, + $('td', undefined, action.label), + $('td', undefined, action.id) + ) + ) + ) + ); + + append(container, details); + return true; + } + private renderColorThemes(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean { const contrib = manifest.contributes?.themes || []; if (!contrib.length) { diff --git a/src/vs/workbench/services/authentication/browser/authenticationService.ts b/src/vs/workbench/services/authentication/browser/authenticationService.ts index 8791faa4681..5b609209058 100644 --- a/src/vs/workbench/services/authentication/browser/authenticationService.ts +++ b/src/vs/workbench/services/authentication/browser/authenticationService.ts @@ -18,6 +18,11 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IProductService } from 'vs/platform/product/common/productService'; import { isString } from 'vs/base/common/types'; +import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { flatten } from 'vs/base/common/arrays'; +import { isFalsyOrWhitespace } from 'vs/base/common/strings'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; export function getAuthenticationProviderActivationEvent(id: string): string { return `onAuthenticationRequest:${id}`; } @@ -55,6 +60,10 @@ export interface IAuthenticationService { readonly onDidUnregisterAuthenticationProvider: Event; readonly onDidChangeSessions: Event<{ providerId: string, label: string, event: AuthenticationSessionsChangeEvent }>; + + declaredProviders: AuthenticationProviderInformation[]; + readonly onDidChangeDeclaredProviders: Event; + getSessions(providerId: string): Promise>; getLabel(providerId: string): string; supportsMultipleAccounts(providerId: string): boolean; @@ -96,6 +105,30 @@ CommandsRegistry.registerCommand('workbench.getCodeExchangeProxyEndpoints', func return environmentService.options?.codeExchangeProxyEndpoints; }); +const authenticationDefinitionSchema: IJSONSchema = { + type: 'object', + additionalProperties: false, + properties: { + id: { + type: 'string', + description: nls.localize('authentication.id', 'The id of the authentication provider.') + }, + label: { + type: 'string', + description: nls.localize('authentication.label', 'The human readable name of the authentication provider.'), + } + } +}; + +const authenticationExtPoint = ExtensionsRegistry.registerExtensionPoint({ + extensionPoint: 'authentication', + jsonSchema: { + description: nls.localize('authenticationExtensionPoint', 'Contributes authentication'), + type: 'array', + items: authenticationDefinitionSchema + } +}); + export class AuthenticationService extends Disposable implements IAuthenticationService { declare readonly _serviceBrand: undefined; private _placeholderMenuItem: IDisposable | undefined; @@ -105,6 +138,11 @@ export class AuthenticationService extends Disposable implements IAuthentication private _authenticationProviders: Map = new Map(); + /** + * All providers that have been statically declared by extensions. These may not be registered. + */ + declaredProviders: AuthenticationProviderInformation[] = []; + private _onDidRegisterAuthenticationProvider: Emitter = this._register(new Emitter()); readonly onDidRegisterAuthenticationProvider: Event = this._onDidRegisterAuthenticationProvider.event; @@ -114,7 +152,13 @@ export class AuthenticationService extends Disposable implements IAuthentication private _onDidChangeSessions: Emitter<{ providerId: string, label: string, event: AuthenticationSessionsChangeEvent }> = this._register(new Emitter<{ providerId: string, label: string, event: AuthenticationSessionsChangeEvent }>()); readonly onDidChangeSessions: Event<{ providerId: string, label: string, event: AuthenticationSessionsChangeEvent }> = this._onDidChangeSessions.event; - constructor(@IActivityService private readonly activityService: IActivityService) { + private _onDidChangeDeclaredProviders: Emitter = this._register(new Emitter()); + readonly onDidChangeDeclaredProviders: Event = this._onDidChangeDeclaredProviders.event; + + constructor( + @IActivityService private readonly activityService: IActivityService, + @IExtensionService private readonly extensionService: IExtensionService + ) { super(); this._placeholderMenuItem = MenuRegistry.appendMenuItem(MenuId.AccountsContext, { command: { @@ -123,6 +167,38 @@ export class AuthenticationService extends Disposable implements IAuthentication precondition: ContextKeyExpr.false() }, }); + + authenticationExtPoint.setHandler((extensions, { added, removed }) => { + added.forEach(point => { + for (const provider of point.value) { + if (isFalsyOrWhitespace(provider.id)) { + point.collector.error(nls.localize('authentication.missingId', 'An authentication contribution must specify an id.')); + continue; + } + + if (isFalsyOrWhitespace(provider.label)) { + point.collector.error(nls.localize('authentication.missingLabel', 'An authentication contribution must specify a label.')); + continue; + } + + if (!this.declaredProviders.some(p => p.id === provider.id)) { + this.declaredProviders.push(provider); + } else { + point.collector.error(nls.localize('authentication.idConflict', "This authentication id '{0}' has already been registered", provider.id)); + } + } + }); + + const removedExtPoints = flatten(removed.map(r => r.value)); + removedExtPoints.forEach(point => { + const index = this.declaredProviders.findIndex(provider => provider.id === point.id); + if (index > -1) { + this.declaredProviders.splice(index, 1); + } + }); + + this._onDidChangeDeclaredProviders.fire(this.declaredProviders); + }); } getProviderIds(): string[] { @@ -339,11 +415,11 @@ export class AuthenticationService extends Disposable implements IAuthentication } } getLabel(id: string): string { - const authProvider = this._authenticationProviders.get(id); + const authProvider = this.declaredProviders.find(provider => provider.id === id); if (authProvider) { return authProvider.label; } else { - throw new Error(`No authentication provider '${id}' is currently registered.`); + throw new Error(`No authentication provider '${id}' has been declared.`); } } @@ -357,6 +433,8 @@ export class AuthenticationService extends Disposable implements IAuthentication } async getSessions(id: string): Promise> { + await this.extensionService.activateByEvent(getAuthenticationProviderActivationEvent(id)); + const authProvider = this._authenticationProviders.get(id); if (authProvider) { return await authProvider.getSessions(); @@ -366,6 +444,8 @@ export class AuthenticationService extends Disposable implements IAuthentication } async login(id: string, scopes: string[]): Promise { + await this.extensionService.activateByEvent(getAuthenticationProviderActivationEvent(id)); + const authProvider = this._authenticationProviders.get(id); if (authProvider) { return authProvider.login(scopes); From 8da30f9ec104a011b9ac7479fd456b246d1a1b9c Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Tue, 25 Aug 2020 00:47:23 -0400 Subject: [PATCH 613/736] Adds title description to view pane containers --- src/vs/base/browser/ui/splitview/paneview.ts | 1 + .../browser/parts/views/media/paneviewlet.css | 20 +++++++++++++ .../browser/parts/views/viewPaneContainer.ts | 20 ++++++++++++- .../timeline/browser/media/timelinePane.css | 20 ------------- .../contrib/timeline/browser/timelinePane.ts | 30 ++++++++----------- 5 files changed, 52 insertions(+), 39 deletions(-) diff --git a/src/vs/base/browser/ui/splitview/paneview.ts b/src/vs/base/browser/ui/splitview/paneview.ts index 292764c8807..fa034af58f2 100644 --- a/src/vs/base/browser/ui/splitview/paneview.ts +++ b/src/vs/base/browser/ui/splitview/paneview.ts @@ -24,6 +24,7 @@ export interface IPaneOptions { expanded?: boolean; orientation?: Orientation; title: string; + titleDescription?: string; } export interface IPaneStyles { diff --git a/src/vs/workbench/browser/parts/views/media/paneviewlet.css b/src/vs/workbench/browser/parts/views/media/paneviewlet.css index 3adeb773148..fea8488c9c1 100644 --- a/src/vs/workbench/browser/parts/views/media/paneviewlet.css +++ b/src/vs/workbench/browser/parts/views/media/paneviewlet.css @@ -38,6 +38,26 @@ -webkit-margin-after: 0; } +.monaco-pane-view .pane > .pane-header .description { + display: block; + font-weight: normal; + margin-left: 10px; + opacity: 0.6; + overflow: hidden; + text-overflow: ellipsis; + text-transform: none; + white-space: nowrap; +} + +.monaco-pane-view .pane > .pane-header .description .codicon { + font-size: 9px; + margin-left: 2px; +} + +.monaco-pane-view .pane > .pane-header:not(.expanded) .description { + display: none; +} + .monaco-pane-view .pane.horizontal:not(.expanded) > .pane-header h3.title, .monaco-pane-view .pane.horizontal:not(.expanded) > .pane-header .description { display: none; diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index 81a0bd9617a..471535fd57b 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -184,6 +184,11 @@ export abstract class ViewPane extends Pane implements IView { return this._title; } + private _titleDescription: string | undefined; + public get titleDescription(): string | undefined { + return this._titleDescription; + } + private readonly menuActions: ViewMenuActions; private progressBar!: ProgressBar; private progressIndicator!: IProgressIndicator; @@ -192,6 +197,7 @@ export abstract class ViewPane extends Pane implements IView { private readonly showActionsAlways: boolean = false; private headerContainer?: HTMLElement; private titleContainer?: HTMLElement; + private titleDescriptionContainer?: HTMLElement; private iconContainer?: HTMLElement; protected twistiesContainer?: HTMLElement; @@ -216,6 +222,7 @@ export abstract class ViewPane extends Pane implements IView { this.id = options.id; this._title = options.title; + this._titleDescription = options.titleDescription; this.showActionsAlways = !!options.showActionsAlways; this.focusedViewContextKey = FocusedViewContext.bindTo(contextKeyService); @@ -334,7 +341,7 @@ export abstract class ViewPane extends Pane implements IView { return this.viewDescriptorService.getViewDescriptorById(this.id)?.containerIcon || 'codicon-window'; } - protected renderHeaderTitle(container: HTMLElement, title: string): void { + protected renderHeaderTitle(container: HTMLElement, title: string, description?: string | undefined): void { this.iconContainer = append(container, $('.icon', undefined)); const icon = this.getIcon(); @@ -360,6 +367,8 @@ export abstract class ViewPane extends Pane implements IView { const calculatedTitle = this.calculateTitle(title); this.titleContainer = append(container, $('h3.title', undefined, calculatedTitle)); + this.titleDescriptionContainer = append(container, $('span.description', undefined, this._titleDescription ?? '')); + this.iconContainer.title = calculatedTitle; this.iconContainer.setAttribute('aria-label', calculatedTitle); } @@ -379,6 +388,15 @@ export abstract class ViewPane extends Pane implements IView { this._onDidChangeTitleArea.fire(); } + protected updateTitleDescription(description?: string | undefined): void { + if (this.titleDescriptionContainer) { + this.titleDescriptionContainer.textContent = description ?? ''; + } + + this._titleDescription = description; + this._onDidChangeTitleArea.fire(); + } + private calculateTitle(title: string): string { const viewContainer = this.viewDescriptorService.getViewContainerByViewId(this.id)!; const model = this.viewDescriptorService.getViewContainerModel(viewContainer); diff --git a/src/vs/workbench/contrib/timeline/browser/media/timelinePane.css b/src/vs/workbench/contrib/timeline/browser/media/timelinePane.css index 7d965ad50da..1a41705614c 100644 --- a/src/vs/workbench/contrib/timeline/browser/media/timelinePane.css +++ b/src/vs/workbench/contrib/timeline/browser/media/timelinePane.css @@ -7,26 +7,6 @@ position: relative; } -.monaco-workbench .timeline-view.pane-header .description { - display: block; - font-weight: normal; - margin-left: 10px; - opacity: 0.6; - overflow: hidden; - text-overflow: ellipsis; - text-transform: none; - white-space: nowrap; -} - -.monaco-workbench .timeline-view.pane-header:not(.expanded) .description { - display: none; -} - -.monaco-workbench .timeline-view.pane-header .description span.codicon { - font-size: 9px; - margin-left: 2px; -} - .monaco-workbench .timeline-tree-view .message.timeline-subtle { opacity: 0.5; padding: 10px 22px 0 22px; diff --git a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts index 153a46de95e..8ba8fc64af5 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -219,7 +219,6 @@ export class TimelinePane extends ViewPane { private $container!: HTMLElement; private $message!: HTMLDivElement; - private $titleDescription!: HTMLSpanElement; private $tree!: HTMLDivElement; private tree!: WorkbenchObjectTree; private treeRenderer: TimelineTreeRenderer | undefined; @@ -276,7 +275,7 @@ export class TimelinePane extends ViewPane { this._followActiveEditor = value; this.followActiveEditorContext.set(value); - this.titleDescription = this.titleDescription; + this.updateFilename(this._filename); if (value) { this.onActiveEditorChanged(); @@ -315,7 +314,7 @@ export class TimelinePane extends ViewPane { } this.uri = uri; - this.titleDescription = uri ? basename(uri.fsPath) : ''; + this.updateFilename(uri ? basename(uri.fsPath) : undefined); this.treeRenderer?.setUri(uri); this.loadTimeline(true); } @@ -407,17 +406,13 @@ export class TimelinePane extends ViewPane { } } - private _titleDescription: string | undefined; - get titleDescription(): string | undefined { - return this._titleDescription; - } - - set titleDescription(description: string | undefined) { - this._titleDescription = description; - if (this.followActiveEditor || !description) { - this.$titleDescription.textContent = description ?? ''; + private _filename: string | undefined; + updateFilename(filename: string | undefined) { + this._filename = filename; + if (this.followActiveEditor || !filename) { + this.updateTitleDescription(filename); } else { - this.$titleDescription.textContent = `${description} (pinned)`; + this.updateTitleDescription(`${filename} (pinned)`); } } @@ -781,17 +776,17 @@ export class TimelinePane extends ViewPane { this._isEmpty = !this.hasVisibleItems; if (this.uri === undefined) { - this.titleDescription = undefined; + this.updateFilename(undefined); this.message = localize('timeline.editorCannotProvideTimeline', "The active editor cannot provide timeline information."); } else if (this._isEmpty) { if (this.pendingRequests.size !== 0) { this.setLoadingUriMessage(); } else { - this.titleDescription = basename(this.uri.fsPath); + this.updateFilename(basename(this.uri.fsPath)); this.message = localize('timeline.noTimelineInfo', "No timeline information was provided."); } } else { - this.titleDescription = basename(this.uri.fsPath); + this.updateFilename(basename(this.uri.fsPath)); this.message = undefined; } @@ -849,7 +844,6 @@ export class TimelinePane extends ViewPane { super.renderHeaderTitle(container, this.title); DOM.addClass(container, 'timeline-view'); - this.$titleDescription = DOM.append(container, DOM.$('span.description', undefined, this.titleDescription ?? '')); } protected renderBody(container: HTMLElement): void { @@ -956,7 +950,7 @@ export class TimelinePane extends ViewPane { setLoadingUriMessage() { const file = this.uri && basename(this.uri.fsPath); - this.titleDescription = file ?? ''; + this.updateFilename(file); this.message = file ? localize('timeline.loading', "Loading timeline for {0}...", file) : ''; } From 95f0ea4cd806d6b0933f47be1015b83711cb84fb Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Thu, 27 Aug 2020 14:30:08 -0400 Subject: [PATCH 614/736] Adds lazy allocation for the itle desc dom node --- .../browser/parts/views/viewPaneContainer.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index 471535fd57b..fb3ca8ac474 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -341,7 +341,7 @@ export abstract class ViewPane extends Pane implements IView { return this.viewDescriptorService.getViewDescriptorById(this.id)?.containerIcon || 'codicon-window'; } - protected renderHeaderTitle(container: HTMLElement, title: string, description?: string | undefined): void { + protected renderHeaderTitle(container: HTMLElement, title: string): void { this.iconContainer = append(container, $('.icon', undefined)); const icon = this.getIcon(); @@ -367,7 +367,10 @@ export abstract class ViewPane extends Pane implements IView { const calculatedTitle = this.calculateTitle(title); this.titleContainer = append(container, $('h3.title', undefined, calculatedTitle)); - this.titleDescriptionContainer = append(container, $('span.description', undefined, this._titleDescription ?? '')); + + if (this._titleDescription) { + this.setTitleDescription(this._titleDescription, container); + } this.iconContainer.title = calculatedTitle; this.iconContainer.setAttribute('aria-label', calculatedTitle); @@ -388,10 +391,17 @@ export abstract class ViewPane extends Pane implements IView { this._onDidChangeTitleArea.fire(); } - protected updateTitleDescription(description?: string | undefined): void { + private setTitleDescription(description: string | undefined, headerContainer: HTMLElement | undefined = this.headerContainer) { if (this.titleDescriptionContainer) { this.titleDescriptionContainer.textContent = description ?? ''; } + else if (description && headerContainer) { + this.titleDescriptionContainer = append(headerContainer, $('span.description', undefined, description)); + } + } + + protected updateTitleDescription(description?: string | undefined): void { + this.setTitleDescription(description); this._titleDescription = description; this._onDidChangeTitleArea.fire(); From b2693bb7eda4d814282abedc438d1ce0e81c2e7a Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 27 Aug 2020 12:01:11 -0700 Subject: [PATCH 615/736] remove legacy kernel on content provider --- .../api/browser/mainThreadNotebook.ts | 27 +------- .../workbench/api/common/extHost.protocol.ts | 6 +- .../workbench/api/common/extHostNotebook.ts | 52 +------------- .../browser/contrib/status/editorStatus.ts | 21 ------ .../notebook/browser/notebookEditorWidget.ts | 67 +++++-------------- .../notebook/browser/notebookServiceImpl.ts | 35 ---------- .../notebook/common/notebookProvider.ts | 1 - .../notebook/common/notebookService.ts | 11 +-- 8 files changed, 23 insertions(+), 197 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 91d4c528fde..7b67595e6ac 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -17,7 +17,7 @@ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; -import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, DisplayOrderKey, ICellEditOperation, IEditor, INotebookDocumentFilter, INotebookKernelInfo, INotebookKernelInfoDto, NotebookCellMetadata, NotebookCellOutputsSplice, NotebookDocumentMetadata, NOTEBOOK_DISPLAY_ORDER, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, DisplayOrderKey, ICellEditOperation, IEditor, INotebookDocumentFilter, INotebookKernelInfo, NotebookCellMetadata, NotebookCellOutputsSplice, NotebookDocumentMetadata, NOTEBOOK_DISPLAY_ORDER, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, MainContext, MainThreadNotebookShape, NotebookExtensionDescription } from '../common/extHost.protocol'; @@ -376,9 +376,8 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo // } } - async $registerNotebookProvider(_extension: NotebookExtensionDescription, _viewType: string, _supportBackup: boolean, _kernel: INotebookKernelInfoDto | undefined, options: { transientOutputs: boolean; transientMetadata: TransientMetadata }): Promise { + async $registerNotebookProvider(_extension: NotebookExtensionDescription, _viewType: string, _supportBackup: boolean, options: { transientOutputs: boolean; transientMetadata: TransientMetadata }): Promise { const controller: IMainNotebookController = { - kernel: _kernel, supportBackup: _supportBackup, options: options, reloadNotebook: async (mainthreadTextModel: NotebookTextModel) => { @@ -427,24 +426,12 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo resolveNotebookEditor: async (viewType: string, uri: URI, editorId: string) => { await this._proxy.$resolveNotebookEditor(viewType, uri, editorId); }, - executeNotebookByAttachedKernel: async (viewType: string, uri: URI) => { - return this.executeNotebookByAttachedKernel(viewType, uri, undefined); - }, - cancelNotebookByAttachedKernel: async (viewType: string, uri: URI) => { - return this.cancelNotebookByAttachedKernel(viewType, uri, undefined); - }, onDidReceiveMessage: (editorId: string, rendererType: string | undefined, message: unknown) => { this._proxy.$onDidReceiveMessage(editorId, rendererType, message); }, removeNotebookDocument: async (uri: URI) => { return this.removeNotebookTextModel(uri); }, - executeNotebookCell: async (uri: URI, handle: number) => { - return this.executeNotebookByAttachedKernel(_viewType, uri, handle); - }, - cancelNotebookCell: async (uri: URI, handle: number) => { - return this.cancelNotebookByAttachedKernel(_viewType, uri, handle); - }, save: async (uri: URI, token: CancellationToken) => { return this._proxy.$saveNotebook(_viewType, uri, token); }, @@ -567,16 +554,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo } } - async executeNotebookByAttachedKernel(viewType: string, uri: URI, handle: number | undefined): Promise { - this.logService.debug('MainthreadNotebooks#executeNotebookByAttachedKernel', uri.path, handle); - return this._proxy.$executeNotebookByAttachedKernel(viewType, uri, handle); - } - - async cancelNotebookByAttachedKernel(viewType: string, uri: URI, handle: number | undefined): Promise { - this.logService.debug('MainthreadNotebooks#cancelNotebookByAttachedKernel', uri.path, handle); - return this._proxy.$cancelNotebookByAttachedKernel(viewType, uri, handle); - } - async $postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise { const editor = this._notebookService.getNotebookEditor(editorId) as INotebookEditor | undefined; if (editor?.isNotebookEditor) { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 009620eadd1..21ba053aa5f 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -51,7 +51,7 @@ import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; import { TunnelOptions } from 'vs/platform/remote/common/tunnel'; import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline'; import { revive } from 'vs/base/common/marshalling'; -import { IProcessedOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEvent, NotebookDataDto, INotebookKernelInfoDto, IMainCellDto, INotebookDocumentFilter, INotebookKernelInfoDto2, TransientMetadata, INotebookCellStatusBarEntry } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { IProcessedOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEvent, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, INotebookKernelInfoDto2, TransientMetadata, INotebookCellStatusBarEntry } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; import { Dto } from 'vs/base/common/types'; import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; @@ -722,7 +722,7 @@ export type NotebookCellOutputsSplice = [ export type INotebookCellStatusBarEntryDto = Dto; export interface MainThreadNotebookShape extends IDisposable { - $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, supportBackup: boolean, kernelInfoDto: INotebookKernelInfoDto | undefined, options: { transientOutputs: boolean; transientMetadata: TransientMetadata }): Promise; + $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, supportBackup: boolean, options: { transientOutputs: boolean; transientMetadata: TransientMetadata }): Promise; $onNotebookChange(viewType: string, resource: UriComponents): Promise; $unregisterNotebookProvider(viewType: string): Promise; $registerNotebookKernel(extension: NotebookExtensionDescription, id: string, label: string, selectors: (string | IRelativePattern)[], preloads: UriComponents[]): Promise; @@ -1658,8 +1658,6 @@ export interface ExtHostNotebookShape { $resolveNotebookEditor(viewType: string, uri: UriComponents, editorId: string): Promise; $provideNotebookKernels(handle: number, uri: UriComponents, token: CancellationToken): Promise; $resolveNotebookKernel(handle: number, editorId: string, uri: UriComponents, kernelId: string, token: CancellationToken): Promise; - $executeNotebookByAttachedKernel(viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise; - $cancelNotebookByAttachedKernel(viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise; $executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise; $cancelNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise; $executeNotebook2(kernelId: string, viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 17db2365cf1..c9b0f4dd564 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -862,7 +862,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN private static _notebookKernelProviderHandlePool: number = 0; private readonly _proxy: MainThreadNotebookShape; - private readonly _notebookContentProviders = new Map(); + private readonly _notebookContentProviders = new Map(); private readonly _notebookKernels = new Map(); private readonly _notebookKernelProviders = new Map(); private readonly _documents = new ResourceMap(); @@ -948,7 +948,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN registerNotebookContentProvider( extension: IExtensionDescription, viewType: string, - provider: vscode.NotebookContentProvider & { kernel?: vscode.NotebookKernel }, + provider: vscode.NotebookContentProvider, options?: { transientOutputs: boolean; transientMetadata: { [K in keyof NotebookCellMetadata]?: boolean }; @@ -959,10 +959,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN throw new Error(`Notebook provider for '${viewType}' already registered`); } - // if ((provider).executeCell) { - // throw new Error('NotebookContentKernel.executeCell is removed, please use vscode.notebook.registerNotebookKernel instead.'); - // } - this._notebookContentProviders.set(viewType, { extension, provider }); const listener = provider.onDidChangeNotebook @@ -984,7 +980,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN const supportBackup = !!provider.backupNotebook; - this._proxy.$registerNotebookProvider({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, viewType, supportBackup, provider.kernel ? { id: viewType, label: provider.kernel.label, extensionLocation: extension.extensionLocation, preloads: provider.kernel.preloads } : undefined, { transientOutputs: options?.transientOutputs || false, transientMetadata: options?.transientMetadata || {} }); + this._proxy.$registerNotebookProvider({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, viewType, supportBackup, { transientOutputs: options?.transientOutputs || false, transientMetadata: options?.transientMetadata || {} }); return new extHostTypes.Disposable(() => { listener.dispose(); @@ -1123,48 +1119,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN await provider.provider.resolveNotebook(document.notebookDocument, webComm.contentProviderComm); } - async $executeNotebookByAttachedKernel(viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise { - const document = this._documents.get(URI.revive(uri)); - - if (!document) { - return; - } - - if (this._notebookContentProviders.has(viewType)) { - const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined; - const provider = this._notebookContentProviders.get(viewType)!.provider; - - if (provider.kernel) { - if (cell) { - return withToken(token => (provider.kernel!.executeCell as any)(document, cell, token)); - } else { - return withToken(token => (provider.kernel!.executeAllCells as any)(document, token)); - } - } - } - } - - async $cancelNotebookByAttachedKernel(viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise { - const document = this._documents.get(URI.revive(uri)); - - if (!document) { - return; - } - - if (this._notebookContentProviders.has(viewType)) { - const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined; - const provider = this._notebookContentProviders.get(viewType)!.provider; - - if (provider.kernel) { - if (cell) { - return provider.kernel.cancelCellExecution(document.notebookDocument, cell.cell); - } else { - return provider.kernel.cancelAllCellsExecution(document.notebookDocument); - } - } - } - } - async $executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise { await this._withAdapter(handle, uri, async (adapter, document) => { const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts b/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts index bb6be90bb17..dca894ed1b9 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts @@ -76,27 +76,6 @@ registerAction2(class extends Action2 { }; }); - const provider = notebookService.getContributedNotebookProviders(editor.viewModel!.uri)[0]; - - if (provider.kernel) { - picks.unshift({ - id: provider.id, - label: provider.displayName, - picked: !activeKernel, // no active kernel, the builtin kernel of the provider is used - description: activeKernel === undefined - ? nls.localize('currentActiveBuiltinKernel', " (Currently Active)") - : '', - kernelProviderId: provider.providerExtensionId, - run: () => { - editor.activeKernel = undefined; - }, - buttons: [{ - iconClass: 'codicon-settings-gear', - tooltip: nls.localize('notebook.promptKernel.setDefaultTooltip', "Set as default kernel provider for '{0}'", editor.viewModel!.viewType) - }] - }); - } - const picker = quickInputService.createQuickPick<(IQuickPickItem & { run(): void; kernelProviderId?: string })>(); picker.items = picks; picker.activeItems = picks.filter(pick => (pick as IQuickPickItem).picked) as (IQuickPickItem & { run(): void; kernelProviderId?: string; })[]; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index d113667a8d1..b2123839f92 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -56,9 +56,6 @@ import { IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IAction, Separator } from 'vs/base/common/actions'; -import { isMacintosh, isNative } from 'vs/base/common/platform'; -import { getTitleBarStyle } from 'vs/platform/windows/common/windows'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { CellDragAndDropController } from 'vs/workbench/contrib/notebook/browser/view/renderers/dnd'; const $ = DOM.$; @@ -222,7 +219,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor @IStorageService storageService: IStorageService, @INotebookService private notebookService: INotebookService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, @IContextKeyService readonly contextKeyService: IContextKeyService, @ILayoutService private readonly layoutService: ILayoutService, @IContextMenuService private readonly contextMenuService: IContextMenuService, @@ -704,10 +700,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return; } - if (provider.kernel && (availableKernels.length + availableKernels2.length) > 0) { - this._notebookHasMultipleKernels!.set(true); - this.multipleKernelsAvailable = true; - } else if ((availableKernels.length + availableKernels2.length) > 1) { + if ((availableKernels.length + availableKernels2.length) > 1) { this._notebookHasMultipleKernels!.set(true); this.multipleKernelsAvailable = true; } else { @@ -715,14 +708,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this.multipleKernelsAvailable = false; } - // @deprecated - if (provider && provider.kernel) { - // it has a builtin kernel, don't automatically choose a kernel - await this._loadKernelPreloads(provider.providerExtensionLocation, provider.kernel); - tokenSource.dispose(); - return; - } - const activeKernelStillExist = [...availableKernels2, ...availableKernels].find(kernel => kernel.id === this.activeKernel?.id && this.activeKernel?.id !== undefined); if (activeKernelStillExist) { @@ -1429,15 +1414,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private async _cancelNotebookExecution(): Promise { const provider = this.notebookService.getContributedNotebookProviders(this.viewModel!.uri)[0]; - if (provider) { - const viewType = provider.id; - const notebookUri = this._notebookViewModel!.uri; - - if (this._activeKernel) { - await (this._activeKernel as INotebookKernelInfo2).cancelNotebookCell!(this._notebookViewModel!.uri, undefined); - } else if (provider.kernel) { - return await this.notebookService.cancelNotebook(viewType, notebookUri); - } + if (provider && this._activeKernel) { + await (this._activeKernel as INotebookKernelInfo2).cancelNotebookCell!(this._notebookViewModel!.uri, undefined); } } @@ -1451,23 +1429,16 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private async _executeNotebook(): Promise { const provider = this.notebookService.getContributedNotebookProviders(this.viewModel!.uri)[0]; - if (provider) { - const viewType = provider.id; - const notebookUri = this._notebookViewModel!.uri; - - if (this._activeKernel) { - // TODO@rebornix temp any cast, should be removed once we remove legacy kernel support - if ((this._activeKernel as INotebookKernelInfo2).executeNotebookCell) { - if (this._activeKernelResolvePromise) { - await this._activeKernelResolvePromise; - } - - await (this._activeKernel as INotebookKernelInfo2).executeNotebookCell!(this._notebookViewModel!.uri, undefined); - } else { - await this.notebookService.executeNotebook2(this._notebookViewModel!.viewType, this._notebookViewModel!.uri, this._activeKernel.id); + if (provider && this._activeKernel) { + // TODO@rebornix temp any cast, should be removed once we remove legacy kernel support + if ((this._activeKernel as INotebookKernelInfo2).executeNotebookCell) { + if (this._activeKernelResolvePromise) { + await this._activeKernelResolvePromise; } - } else if (provider.kernel) { - return await this.notebookService.executeNotebook(viewType, notebookUri); + + await (this._activeKernel as INotebookKernelInfo2).executeNotebookCell!(this._notebookViewModel!.uri, undefined); + } else { + await this.notebookService.executeNotebook2(this._notebookViewModel!.viewType, this._notebookViewModel!.uri, this._activeKernel.id); } } } @@ -1491,15 +1462,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private async _cancelNotebookCell(cell: ICellViewModel): Promise { const provider = this.notebookService.getContributedNotebookProviders(this.viewModel!.uri)[0]; - if (provider) { - const viewType = provider.id; - const notebookUri = this._notebookViewModel!.uri; - - if (this._activeKernel) { - return await (this._activeKernel as INotebookKernelInfo2).cancelNotebookCell!(this._notebookViewModel!.uri, cell.handle); - } else if (provider.kernel) { - return await this.notebookService.cancelNotebookCell(viewType, notebookUri, cell.handle); - } + if (provider && this._activeKernel) { + return await (this._activeKernel as INotebookKernelInfo2).cancelNotebookCell!(this._notebookViewModel!.uri, cell.handle); } } @@ -1530,9 +1494,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return await this.notebookService.executeNotebookCell2(viewType, notebookUri, cell.handle, this._activeKernel.id); } - } else if (provider.kernel) { - return await this.notebookService.executeNotebookCell(viewType, notebookUri, cell.handle); } + } } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index bff66c05e13..487636173bb 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -548,7 +548,6 @@ export class NotebookService extends Disposable implements INotebookService, ICu registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController) { this._notebookProviders.set(viewType, { extensionData, controller }); - this.notebookProviderInfoStore.get(viewType)!.kernel = controller.kernel; this._onDidChangeViewTypes.fire(); } @@ -820,40 +819,6 @@ export class NotebookService extends Disposable implements INotebookService, ICu return this.notebookRenderersInfoStore.getContributedRenderer(mimeType); } - async executeNotebook(viewType: string, uri: URI): Promise { - const provider = this._notebookProviders.get(viewType); - - if (provider) { - return provider.controller.executeNotebookByAttachedKernel(viewType, uri); - } - - return; - } - - async executeNotebookCell(viewType: string, uri: URI, handle: number): Promise { - const provider = this._notebookProviders.get(viewType); - if (provider) { - await provider.controller.executeNotebookCell(uri, handle); - } - } - - async cancelNotebook(viewType: string, uri: URI): Promise { - const provider = this._notebookProviders.get(viewType); - - if (provider) { - return provider.controller.cancelNotebookByAttachedKernel(viewType, uri); - } - - return; - } - - async cancelNotebookCell(viewType: string, uri: URI, handle: number): Promise { - const provider = this._notebookProviders.get(viewType); - if (provider) { - await provider.controller.cancelNotebookCell(uri, handle); - } - } - async executeNotebook2(viewType: string, uri: URI, kernelId: string): Promise { const kernel = this._notebookKernels.get(kernelId); if (kernel) { diff --git a/src/vs/workbench/contrib/notebook/common/notebookProvider.ts b/src/vs/workbench/contrib/notebook/common/notebookProvider.ts index 2ffcad9a909..cab62ec09c9 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookProvider.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookProvider.ts @@ -36,7 +36,6 @@ export class NotebookProviderInfo implements NotebookEditorDescriptor { readonly providerDescription?: string; readonly providerDisplayName: string; readonly providerExtensionLocation: URI; - kernel?: INotebookKernelInfoDto; constructor(descriptor: NotebookEditorDescriptor) { this.id = descriptor.id; diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index 9e7a32ec7a1..32ae67d995d 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -9,7 +9,7 @@ import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/noteb import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.protocol'; import { Event } from 'vs/base/common/event'; import { - INotebookTextModel, INotebookRendererInfo, INotebookKernelInfo, INotebookKernelInfoDto, + INotebookTextModel, INotebookRendererInfo, INotebookKernelInfo, IEditor, ICellEditOperation, NotebookCellOutputsSplice, INotebookKernelProvider, INotebookKernelInfo2, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; @@ -22,17 +22,12 @@ import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common export const INotebookService = createDecorator('notebookService'); export interface IMainNotebookController { - kernel: INotebookKernelInfoDto | undefined; supportBackup: boolean; options: { transientOutputs: boolean; transientMetadata: TransientMetadata; }; createNotebook(textModel: NotebookTextModel, editorId?: string, backupId?: string): Promise; reloadNotebook(mainthreadTextModel: NotebookTextModel): Promise; resolveNotebookEditor(viewType: string, uri: URI, editorId: string): Promise; - executeNotebookByAttachedKernel(viewType: string, uri: URI): Promise; - cancelNotebookByAttachedKernel(viewType: string, uri: URI): Promise; onDidReceiveMessage(editorId: string, rendererType: string | undefined, message: any): void; - executeNotebookCell(uri: URI, handle: number): Promise; - cancelNotebookCell(uri: URI, handle: number): Promise; removeNotebookDocument(uri: URI): Promise; save(uri: URI, token: CancellationToken): Promise; saveAs(uri: URI, target: URI, token: CancellationToken): Promise; @@ -65,10 +60,6 @@ export interface INotebookService { resolveNotebook(viewType: string, uri: URI, forceReload: boolean, editorId?: string, backupId?: string): Promise; getNotebookTextModel(uri: URI): NotebookTextModel | undefined; - executeNotebook(viewType: string, uri: URI): Promise; - cancelNotebook(viewType: string, uri: URI): Promise; - executeNotebookCell(viewType: string, uri: URI, handle: number): Promise; - cancelNotebookCell(viewType: string, uri: URI, handle: number): Promise; executeNotebook2(viewType: string, uri: URI, kernelId: string): Promise; executeNotebookCell2(viewType: string, uri: URI, handle: number, kernelId: string): Promise; getContributedNotebookProviders(resource: URI): readonly NotebookProviderInfo[]; From b991349b3332534789ccb616dd84acaff12f8c59 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Thu, 27 Aug 2020 12:04:31 -0700 Subject: [PATCH 616/736] Mark onDidChangeAuthenticationProviders as deprecated in proposed API --- src/vs/vscode.proposed.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index cf7cd17fb93..0d67860afe2 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -112,6 +112,7 @@ declare module 'vscode' { export function registerAuthenticationProvider(provider: AuthenticationProvider): Disposable; /** + * @deprecated - getSession should now trigger extension activation. * Fires with the provider id that was registered or unregistered. */ export const onDidChangeAuthenticationProviders: Event; From 2f88d58859a6ba72b59c0d2f8d0811c11bb62fd0 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 27 Aug 2020 12:05:58 -0700 Subject: [PATCH 617/736] rename :lipstick: --- .../contrib/notebook/browser/notebookEditorWidget.ts | 4 ++-- .../workbench/contrib/notebook/browser/notebookServiceImpl.ts | 4 ++-- src/vs/workbench/contrib/notebook/common/notebookService.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index b2123839f92..df7797413d7 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1438,7 +1438,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor await (this._activeKernel as INotebookKernelInfo2).executeNotebookCell!(this._notebookViewModel!.uri, undefined); } else { - await this.notebookService.executeNotebook2(this._notebookViewModel!.viewType, this._notebookViewModel!.uri, this._activeKernel.id); + await this.notebookService.executeNotebook(this._notebookViewModel!.viewType, this._notebookViewModel!.uri, this._activeKernel.id); } } } @@ -1492,7 +1492,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor await (this._activeKernel as INotebookKernelInfo2).executeNotebookCell!(this._notebookViewModel!.uri, cell.handle); } else { - return await this.notebookService.executeNotebookCell2(viewType, notebookUri, cell.handle, this._activeKernel.id); + return await this.notebookService.executeNotebookCell(viewType, notebookUri, cell.handle, this._activeKernel.id); } } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 487636173bb..dba22f1d968 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -819,14 +819,14 @@ export class NotebookService extends Disposable implements INotebookService, ICu return this.notebookRenderersInfoStore.getContributedRenderer(mimeType); } - async executeNotebook2(viewType: string, uri: URI, kernelId: string): Promise { + async executeNotebook(viewType: string, uri: URI, kernelId: string): Promise { const kernel = this._notebookKernels.get(kernelId); if (kernel) { await kernel.executeNotebook(viewType, uri, undefined); } } - async executeNotebookCell2(viewType: string, uri: URI, handle: number, kernelId: string): Promise { + async executeNotebookCell(viewType: string, uri: URI, handle: number, kernelId: string): Promise { const kernel = this._notebookKernels.get(kernelId); if (kernel) { await kernel.executeNotebook(viewType, uri, handle); diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index 32ae67d995d..c58730d0491 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -60,8 +60,8 @@ export interface INotebookService { resolveNotebook(viewType: string, uri: URI, forceReload: boolean, editorId?: string, backupId?: string): Promise; getNotebookTextModel(uri: URI): NotebookTextModel | undefined; - executeNotebook2(viewType: string, uri: URI, kernelId: string): Promise; - executeNotebookCell2(viewType: string, uri: URI, handle: number, kernelId: string): Promise; + executeNotebook(viewType: string, uri: URI, kernelId: string): Promise; + executeNotebookCell(viewType: string, uri: URI, handle: number, kernelId: string): Promise; getContributedNotebookProviders(resource: URI): readonly NotebookProviderInfo[]; getContributedNotebookProvider(viewType: string): NotebookProviderInfo | undefined; getNotebookProviderResourceRoots(): URI[]; From aa6b2f163dad27d8e5d011ed36fb77e536a14e9d Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 27 Aug 2020 12:38:33 -0700 Subject: [PATCH 618/736] Initial terminal welcome support --- .../api/browser/mainThreadTerminalService.ts | 4 ++ .../workbench/api/common/extHost.protocol.ts | 1 + .../api/common/extHostTerminalService.ts | 8 ++++ .../api/node/extHostTerminalService.ts | 2 +- .../terminal/browser/terminal.contribution.ts | 4 +- .../contrib/terminal/browser/terminal.ts | 3 ++ .../terminal/browser/terminalActions.ts | 7 ++-- .../terminal/browser/terminalService.ts | 28 +++++++++---- .../contrib/terminal/browser/terminalView.ts | 42 +++++++++++++------ .../contrib/terminal/common/terminal.ts | 4 +- 10 files changed, 76 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index b64f47fad6d..c5c03a86786 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -178,6 +178,10 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._linkProvider = undefined; } + public $registerProcessSupport(isSupported: boolean): void { + this._terminalService.registerProcessSupport(isSupported); + } + private _onActiveTerminalChanged(terminalId: number | null): void { this._proxy.$acceptActiveTerminalChanged(terminalId); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index e09f81490e8..9baee2b3b22 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -450,6 +450,7 @@ export interface MainThreadTerminalServiceShape extends IDisposable { $stopSendingDataEvents(): void; $startLinkProvider(): void; $stopLinkProvider(): void; + $registerProcessSupport(isSupported: boolean): void; $setEnvironmentVariableCollection(extensionIdentifier: string, persistent: boolean, collection: ISerializableEnvironmentVariableCollection | undefined): void; // Process diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index ad449efd0da..7107003f8f7 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -339,6 +339,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ public get onDidWriteTerminalData(): Event { return this._onDidWriteTerminalData && this._onDidWriteTerminalData.event; } constructor( + supportsProcesses: boolean, @IExtHostRpcService extHostRpc: IExtHostRpcService ) { this._proxy = extHostRpc.getProxy(MainContext.MainThreadTerminalService); @@ -347,6 +348,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ onFirstListenerAdd: () => this._proxy.$startSendingDataEvents(), onLastListenerRemove: () => this._proxy.$stopSendingDataEvents() }); + this._proxy.$registerProcessSupport(supportsProcesses); } public abstract createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal; @@ -805,6 +807,12 @@ export class EnvironmentVariableCollection implements vscode.EnvironmentVariable } export class WorkerExtHostTerminalService extends BaseExtHostTerminalService { + constructor( + @IExtHostRpcService extHostRpc: IExtHostRpcService + ) { + super(false, extHostRpc); + } + public createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal { throw new NotSupportedError(); } diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index c777688550a..54d99efad93 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -39,7 +39,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { @IExtHostDocumentsAndEditors private _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors, @ILogService private _logService: ILogService ) { - super(extHostRpc); + super(true, extHostRpc); this._updateLastActiveWorkspace(); this._updateVariableResolver(); this._registerListeners(); diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index a2915b88d67..45fe642e2df 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -21,7 +21,7 @@ import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/wor import { Extensions as ViewContainerExtensions, IViewContainersRegistry, ViewContainerLocation, IViewsRegistry } from 'vs/workbench/common/views'; import { registerTerminalActions, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewTerminalAction, KillTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, ToggleTerminalAction, terminalSendSequenceCommand } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { TerminalViewPane } from 'vs/workbench/contrib/terminal/browser/terminalView'; -import { KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_VIEW_ID, TERMINAL_ACTION_CATEGORY, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal'; +import { KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_VIEW_ID, TERMINAL_ACTION_CATEGORY, TERMINAL_COMMAND_ID, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED } from 'vs/workbench/contrib/terminal/common/terminal'; import { registerColors } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { setupTerminalCommands } from 'vs/workbench/contrib/terminal/browser/terminalCommands'; import { setupTerminalMenu } from 'vs/workbench/contrib/terminal/common/terminalMenu'; @@ -88,7 +88,7 @@ actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(KillTerminalAct actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(CreateNewTerminalAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_BACKTICK, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.US_BACKTICK } -}), 'Terminal: Create New Integrated Terminal', category); +}), 'Terminal: Create New Integrated Terminal', category, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED); actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(SelectAllTerminalAction, { // Don't use ctrl+a by default as that would override the common go to start // of prompt shell binding diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index a1f25f55fb9..a7db7423cfa 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -76,6 +76,7 @@ export interface ITerminalService { configHelper: ITerminalConfigHelper; terminalInstances: ITerminalInstance[]; terminalTabs: ITerminalTab[]; + isProcessSupportRegistered: boolean; onActiveTabChanged: Event; onTabDisposed: Event; @@ -90,6 +91,7 @@ export interface ITerminalService { onInstanceTitleChanged: Event; onActiveInstanceChanged: Event; onRequestAvailableShells: Event; + onDidRegisterProcessSupport: Event; /** * Creates a terminal. @@ -136,6 +138,7 @@ export interface ITerminalService { findNext(): void; findPrevious(): void; + registerProcessSupport(isSupported: boolean): void; /** * Registers a link provider that enables integrators to add links to the terminal. * @param linkProvider When registered, the link provider is asked whenever a cell is hovered diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index dd4b2216ff2..bbb14f6a0b1 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -6,7 +6,7 @@ import { Action, IAction } from 'vs/base/common/actions'; import { EndOfLinePreference } from 'vs/editor/common/model'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { TERMINAL_VIEW_ID, ITerminalConfigHelper, TitleEventSource, TERMINAL_COMMAND_ID, KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED, TERMINAL_ACTION_CATEGORY, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FIND_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TERMINAL_VIEW_ID, ITerminalConfigHelper, TitleEventSource, TERMINAL_COMMAND_ID, KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED, TERMINAL_ACTION_CATEGORY, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FIND_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED } from 'vs/workbench/contrib/terminal/common/terminal'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -367,7 +367,7 @@ export class SwitchTerminalActionViewItem extends SelectActionViewItem { this._register(_terminalService.onActiveTabChanged(this._updateItems, this)); this._register(_terminalService.onInstanceTitleChanged(this._updateItems, this)); this._register(_terminalService.onTabDisposed(this._updateItems, this)); - this._register(attachSelectBoxStyler(this.selectBox, _themeService)); + this._register(attachSelectBoxStyler(this.selectBox, this._themeService)); } render(container: HTMLElement): void { @@ -437,7 +437,8 @@ export function registerTerminalActions() { id: TERMINAL_COMMAND_ID.NEW_IN_ACTIVE_WORKSPACE, title: { value: localize('workbench.action.terminal.newInActiveWorkspace', "Create New Integrated Terminal (In Active Workspace)"), original: 'Create New Integrated Terminal (In Active Workspace)' }, f1: true, - category + category, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } async run(accessor: ServicesAccessor) { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 15154160cbd..de6a1f38ff7 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { TERMINAL_VIEW_ID, IShellLaunchConfig, ITerminalConfigHelper, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, ITerminalProcessExtHostProxy, IShellDefinition, LinuxDistro, KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE, ITerminalLaunchError, ITerminalNativeWindowsDelegate } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TERMINAL_VIEW_ID, IShellLaunchConfig, ITerminalConfigHelper, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, ITerminalProcessExtHostProxy, IShellDefinition, LinuxDistro, KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE, ITerminalLaunchError, ITerminalNativeWindowsDelegate } from 'vs/workbench/contrib/terminal/common/terminal'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; @@ -23,12 +23,13 @@ import { Event, Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; -import { isWindows, isMacintosh, OperatingSystem } from 'vs/base/common/platform'; +import { isWindows, isMacintosh, OperatingSystem, isWeb } from 'vs/base/common/platform'; import { basename } from 'vs/base/common/path'; import { find } from 'vs/base/common/arrays'; import { timeout } from 'vs/base/common/async'; import { IViewsService, ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; interface IExtHostReadyEntry { promise: Promise; @@ -52,10 +53,12 @@ export class TerminalService implements ITerminalService { private _activeTabIndex: number; private _linkProviders: Set = new Set(); private _linkProviderDisposables: Map = new Map(); + private _processSupportContextKey: IContextKey; public get activeTabIndex(): number { return this._activeTabIndex; } public get terminalInstances(): ITerminalInstance[] { return this._terminalInstances; } public get terminalTabs(): ITerminalTab[] { return this._terminalTabs; } + public get isProcessSupportRegistered(): boolean { return !!this._processSupportContextKey.get(); } private _configHelper: TerminalConfigHelper; private _terminalContainer: HTMLElement | undefined; @@ -91,6 +94,8 @@ export class TerminalService implements ITerminalService { public get onTabDisposed(): Event { return this._onTabDisposed.event; } private readonly _onRequestAvailableShells = new Emitter(); public get onRequestAvailableShells(): Event { return this._onRequestAvailableShells.event; } + private readonly _onDidRegisterProcessSupport = new Emitter(); + public get onDidRegisterProcessSupport(): Event { return this._onDidRegisterProcessSupport.event; } constructor( @IContextKeyService private _contextKeyService: IContextKeyService, @@ -103,7 +108,8 @@ export class TerminalService implements ITerminalService { @IQuickInputService private _quickInputService: IQuickInputService, @IConfigurationService private _configurationService: IConfigurationService, @IViewsService private _viewsService: IViewsService, - @IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService + @IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService ) { this._activeTabIndex = 0; this._isShuttingDown = false; @@ -121,7 +127,9 @@ export class TerminalService implements ITerminalService { }); this.onInstanceLinksReady(instance => this._setInstanceLinkProviders(instance)); - this._handleContextKeys(); + this._handleInstanceContextKeys(); + this._processSupportContextKey = KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED.bindTo(this._contextKeyService); + this._processSupportContextKey.set(!isWeb || this._remoteAgentService.getConnection() !== null); } public setNativeWindowsDelegate(delegate: ITerminalNativeWindowsDelegate): void { @@ -132,13 +140,11 @@ export class TerminalService implements ITerminalService { this._configHelper.setLinuxDistro(linuxDistro); } - private _handleContextKeys(): void { + private _handleInstanceContextKeys(): void { const terminalIsOpenContext = KEYBINDING_CONTEXT_TERMINAL_IS_OPEN.bindTo(this._contextKeyService); - const updateTerminalContextKeys = () => { terminalIsOpenContext.set(this.terminalInstances.length > 0); }; - this.onInstancesChanged(() => updateTerminalContextKeys()); } @@ -411,6 +417,14 @@ export class TerminalService implements ITerminalService { instance.addDisposable(instance.onFocus(this._onActiveInstanceChanged.fire, this._onActiveInstanceChanged)); } + public registerProcessSupport(isSupported: boolean): void { + if (!isSupported) { + return; + } + this._processSupportContextKey.set(isSupported); + this._onDidRegisterProcessSupport.fire(); + } + public registerLinkProvider(linkProvider: ITerminalExternalLinkProvider): IDisposable { const disposables: IDisposable[] = []; this._linkProviders.add(linkProvider); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 55d0d5e1865..c526770faa6 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -19,7 +19,6 @@ import { URI } from 'vs/base/common/uri'; import { TERMINAL_BACKGROUND_COLOR, TERMINAL_BORDER_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { DataTransfers } from 'vs/base/browser/dnd'; import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; -import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; @@ -56,14 +55,25 @@ export class TerminalViewPane extends ViewPane { @IThemeService protected readonly themeService: IThemeService, @ITelemetryService telemetryService: ITelemetryService, @INotificationService private readonly _notificationService: INotificationService, - @IStorageService storageService: IStorageService, @IOpenerService openerService: IOpenerService, ) { super(options, keybindingService, _contextMenuService, configurationService, contextKeyService, viewDescriptorService, _instantiationService, openerService, themeService, telemetryService); + this._terminalService.onDidRegisterProcessSupport(() => { + if (this._actions) { + for (const action of this._actions) { + action.enabled = true; + } + } + this._onDidChangeViewWelcomeState.fire(); + }); } protected renderBody(container: HTMLElement): void { super.renderBody(container); + if (this.shouldShowWelcome()) { + return; + } + this._parentDomElement = container; dom.addClass(this._parentDomElement, 'integrated-terminal'); this._fontStyleElement = document.createElement('style'); @@ -120,6 +130,10 @@ export class TerminalViewPane extends ViewPane { protected layoutBody(height: number, width: number): void { super.layoutBody(height, width); + if (this.shouldShowWelcome()) { + return; + } + this._bodyDimensions.width = width; this._bodyDimensions.height = height; this._terminalService.terminalTabs.forEach(t => t.layout(width, height)); @@ -138,9 +152,12 @@ export class TerminalViewPane extends ViewPane { this._splitTerminalAction, this._instantiationService.createInstance(KillTerminalAction, KillTerminalAction.ID, KillTerminalAction.PANEL_LABEL) ]; - this._actions.forEach(a => { - this._register(a); - }); + for (const action of this._actions) { + if (!this._terminalService.isProcessSupportRegistered) { + action.enabled = false; + } + this._register(action); + } } return this._actions; } @@ -180,7 +197,7 @@ export class TerminalViewPane extends ViewPane { } public getActionViewItem(action: Action): IActionViewItem | undefined { - if (action.id === SwitchTerminalAction.ID) { + if (action.id === SwitchTerminalAction.ID && this._terminalService.isProcessSupportRegistered) { return this._instantiationService.createInstance(SwitchTerminalActionViewItem, action); } @@ -188,10 +205,7 @@ export class TerminalViewPane extends ViewPane { } public focus(): void { - const activeInstance = this._terminalService.getActiveInstance(); - if (activeInstance) { - activeInstance.focusWhenReady(true); - } + this._terminalService.getActiveInstance()?.focusWhenReady(true); } public focusFindWidget() { @@ -331,9 +345,11 @@ export class TerminalViewPane extends ViewPane { theme = this.themeService.getColorTheme(); } - if (this._findWidget) { - this._findWidget.updateTheme(theme); - } + this._findWidget?.updateTheme(theme); + } + + shouldShowWelcome(): boolean { + return !this._terminalService.isProcessSupportRegistered; } } diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 5edbd197dbd..9c4323340a0 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -12,7 +12,7 @@ import { OperatingSystem } from 'vs/base/common/platform'; import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { IExtensionPointDescriptor } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -export const TERMINAL_VIEW_ID = 'workbench.panel.terminal'; +export const TERMINAL_VIEW_ID = 'terminal'; /** A context key that is set when there is at least one opened integrated terminal. */ export const KEYBINDING_CONTEXT_TERMINAL_IS_OPEN = new RawContextKey('terminalIsOpen', false); @@ -46,6 +46,8 @@ export const KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED = new RawContextKey('terminalProcessSupported', false); + export const IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY = 'terminal.integrated.isWorkspaceShellAllowed'; export const NEVER_MEASURE_RENDER_TIME_STORAGE_KEY = 'terminal.integrated.neverMeasureRenderTime'; From a5682a5a467028ca87cc1ff3285a0eae490650f2 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 27 Aug 2020 12:46:46 -0700 Subject: [PATCH 619/736] Require process support for most terminal keybindings --- .../terminal/browser/terminal.contribution.ts | 18 +-- .../terminal/browser/terminalActions.ts | 130 ++++++++++++------ 2 files changed, 95 insertions(+), 53 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 45fe642e2df..9a9fa34d16e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -84,11 +84,11 @@ Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); registerTerminalActions(); const category = TERMINAL_ACTION_CATEGORY; -actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(KillTerminalAction), 'Terminal: Kill the Active Terminal Instance', category); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(KillTerminalAction), 'Terminal: Kill the Active Terminal Instance', category, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED); actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(CreateNewTerminalAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_BACKTICK, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.US_BACKTICK } -}), 'Terminal: Create New Integrated Terminal', category, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED); +}), 'Terminal: Create New Integrated Terminal', category); actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(SelectAllTerminalAction, { // Don't use ctrl+a by default as that would override the common go to start // of prompt shell binding @@ -97,7 +97,7 @@ actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(SelectAllTermin // behavior anyway when handed to xterm.js, having this handled by VS Code // makes it easier for users to see how it works though. mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_A } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Select All', category); +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Select All', category, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED); actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleTerminalAction, { primary: KeyMod.CtrlCmd | KeyCode.US_BACKTICK, mac: { primary: KeyMod.WinCtrl | KeyCode.US_BACKTICK } @@ -107,16 +107,16 @@ actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleTerminalA actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ClearTerminalAction, { primary: 0, mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_K } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KeybindingWeight.WorkbenchContrib + 1), 'Terminal: Clear', category); -actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(SelectDefaultShellWindowsTerminalAction), 'Terminal: Select Default Shell', category); +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KeybindingWeight.WorkbenchContrib + 1), 'Terminal: Clear', category, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(SelectDefaultShellWindowsTerminalAction), 'Terminal: Select Default Shell', category, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED); actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(SplitTerminalAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_5, mac: { primary: KeyMod.CtrlCmd | KeyCode.US_BACKSLASH, secondary: [KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_5] } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Split Terminal', category); -actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(SplitInActiveWorkspaceTerminalAction), 'Terminal: Split Terminal (In Active Workspace)', category); +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Split Terminal', category, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(SplitInActiveWorkspaceTerminalAction), 'Terminal: Split Terminal (In Active Workspace)', category, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED); // Commands might be affected by Web restrictons if (BrowserFeatures.clipboard.writeText) { @@ -124,7 +124,7 @@ if (BrowserFeatures.clipboard.writeText) { primary: KeyMod.CtrlCmd | KeyCode.KEY_C, win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_C, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C] }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C } - }, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FOCUS)), 'Terminal: Copy Selection', category); + }, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FOCUS)), 'Terminal: Copy Selection', category, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED); } function registerSendSequenceKeybinding(text: string, rule: { when?: ContextKeyExpression } & IKeybindings): void { @@ -149,7 +149,7 @@ if (BrowserFeatures.clipboard.readText) { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V] }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V } - }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Paste into Active Terminal', category); + }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Paste into Active Terminal', category, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED); // An extra Windows-only ctrl+v keybinding is used for pwsh that sends ctrl+v directly to the // shell, this gets handled by PSReadLine which properly handles multi-line pastes. This is // disabled in accessibility mode as PowerShell does not run PSReadLine when it detects a screen diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index bbb14f6a0b1..b532ed7aae5 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -437,8 +437,7 @@ export function registerTerminalActions() { id: TERMINAL_COMMAND_ID.NEW_IN_ACTIVE_WORKSPACE, title: { value: localize('workbench.action.terminal.newInActiveWorkspace', "Create New Integrated Terminal (In Active Workspace)"), original: 'Create New Integrated Terminal (In Active Workspace)' }, f1: true, - category, - precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED + category }); } async run(accessor: ServicesAccessor) { @@ -467,7 +466,8 @@ export function registerTerminalActions() { }, when: KEYBINDING_CONTEXT_TERMINAL_FOCUS, weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } async run(accessor: ServicesAccessor) { @@ -492,7 +492,8 @@ export function registerTerminalActions() { }, when: KEYBINDING_CONTEXT_TERMINAL_FOCUS, weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } async run(accessor: ServicesAccessor) { @@ -513,7 +514,8 @@ export function registerTerminalActions() { mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.LeftArrow }, when: KEYBINDING_CONTEXT_TERMINAL_FOCUS, weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } async run(accessor: ServicesAccessor) { @@ -532,7 +534,8 @@ export function registerTerminalActions() { mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.RightArrow }, when: KEYBINDING_CONTEXT_TERMINAL_FOCUS, weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } async run(accessor: ServicesAccessor) { @@ -550,7 +553,8 @@ export function registerTerminalActions() { mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.UpArrow }, when: KEYBINDING_CONTEXT_TERMINAL_FOCUS, weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } async run(accessor: ServicesAccessor) { @@ -568,7 +572,8 @@ export function registerTerminalActions() { mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.DownArrow }, when: KEYBINDING_CONTEXT_TERMINAL_FOCUS, weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } async run(accessor: ServicesAccessor) { @@ -581,7 +586,8 @@ export function registerTerminalActions() { id: TERMINAL_COMMAND_ID.FOCUS, title: { value: localize('workbench.action.terminal.focus', "Focus Terminal"), original: 'Focus Terminal' }, f1: true, - category + category, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } async run(accessor: ServicesAccessor) { @@ -600,7 +606,8 @@ export function registerTerminalActions() { id: TERMINAL_COMMAND_ID.FOCUS_NEXT, title: { value: localize('workbench.action.terminal.focusNext', "Focus Next Terminal"), original: 'Focus Next Terminal' }, f1: true, - category + category, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } async run(accessor: ServicesAccessor) { @@ -615,7 +622,8 @@ export function registerTerminalActions() { id: TERMINAL_COMMAND_ID.FOCUS_PREVIOUS, title: { value: localize('workbench.action.terminal.focusPrevious', "Focus Previous Terminal"), original: 'Focus Previous Terminal' }, f1: true, - category + category, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } async run(accessor: ServicesAccessor) { @@ -630,7 +638,8 @@ export function registerTerminalActions() { id: TERMINAL_COMMAND_ID.RUN_SELECTED_TEXT, title: { value: localize('workbench.action.terminal.runSelectedText', "Run Selected Text In Active Terminal"), original: 'Run Selected Text In Active Terminal' }, f1: true, - category + category, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } async run(accessor: ServicesAccessor) { @@ -660,7 +669,8 @@ export function registerTerminalActions() { id: TERMINAL_COMMAND_ID.RUN_ACTIVE_FILE, title: { value: localize('workbench.action.terminal.runActiveFile', "Run Active File In Active Terminal"), original: 'Run Active File In Active Terminal' }, f1: true, - category + category, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } async run(accessor: ServicesAccessor) { @@ -700,7 +710,8 @@ export function registerTerminalActions() { linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow }, when: KEYBINDING_CONTEXT_TERMINAL_FOCUS, weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -719,7 +730,8 @@ export function registerTerminalActions() { mac: { primary: KeyCode.PageDown }, when: KEYBINDING_CONTEXT_TERMINAL_FOCUS, weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -738,7 +750,8 @@ export function registerTerminalActions() { linux: { primary: KeyMod.Shift | KeyCode.End }, when: KEYBINDING_CONTEXT_TERMINAL_FOCUS, weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -757,7 +770,8 @@ export function registerTerminalActions() { linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow }, when: KEYBINDING_CONTEXT_TERMINAL_FOCUS, weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -776,7 +790,8 @@ export function registerTerminalActions() { mac: { primary: KeyCode.PageUp }, when: KEYBINDING_CONTEXT_TERMINAL_FOCUS, weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -795,7 +810,8 @@ export function registerTerminalActions() { linux: { primary: KeyMod.Shift | KeyCode.Home }, when: KEYBINDING_CONTEXT_TERMINAL_FOCUS, weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -813,7 +829,8 @@ export function registerTerminalActions() { primary: KeyCode.Escape, when: ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED), weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -834,7 +851,8 @@ export function registerTerminalActions() { ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED) ), weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -855,7 +873,8 @@ export function registerTerminalActions() { ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED) ), weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -873,7 +892,8 @@ export function registerTerminalActions() { primary: KeyCode.Escape, when: ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FIND_NOT_VISIBLE), weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -889,7 +909,8 @@ export function registerTerminalActions() { id: TERMINAL_COMMAND_ID.MANAGE_WORKSPACE_SHELL_PERMISSIONS, title: { value: localize('workbench.action.terminal.manageWorkspaceShellPermissions', "Manage Workspace Shell Permissions"), original: 'Manage Workspace Shell Permissions' }, f1: true, - category + category, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -902,7 +923,8 @@ export function registerTerminalActions() { id: TERMINAL_COMMAND_ID.RENAME, title: { value: localize('workbench.action.terminal.rename', "Rename"), original: 'Rename' }, f1: true, - category + category, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } async run(accessor: ServicesAccessor) { @@ -928,7 +950,8 @@ export function registerTerminalActions() { primary: KeyMod.CtrlCmd | KeyCode.KEY_F, when: ContextKeyExpr.or(KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FOCUS), weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -947,7 +970,8 @@ export function registerTerminalActions() { secondary: [KeyMod.Shift | KeyCode.Escape], when: ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE), weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -960,7 +984,8 @@ export function registerTerminalActions() { id: TERMINAL_COMMAND_ID.QUICK_OPEN_TERM, title: { value: localize('quickAccessTerminal', "Switch Active Terminal"), original: 'Switch Active Terminal' }, f1: true, - category + category, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -978,7 +1003,8 @@ export function registerTerminalActions() { mac: { primary: KeyMod.CtrlCmd | KeyCode.UpArrow }, when: ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate()), weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -999,7 +1025,8 @@ export function registerTerminalActions() { mac: { primary: KeyMod.CtrlCmd | KeyCode.DownArrow }, when: ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate()), weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -1020,7 +1047,8 @@ export function registerTerminalActions() { mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow }, when: KEYBINDING_CONTEXT_TERMINAL_FOCUS, weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -1041,7 +1069,8 @@ export function registerTerminalActions() { mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow }, when: KEYBINDING_CONTEXT_TERMINAL_FOCUS, weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -1057,7 +1086,8 @@ export function registerTerminalActions() { id: TERMINAL_COMMAND_ID.SELECT_TO_PREVIOUS_LINE, title: { value: localize('workbench.action.terminal.selectToPreviousLine', "Select To Previous Line"), original: 'Select To Previous Line' }, f1: true, - category + category, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -1073,7 +1103,8 @@ export function registerTerminalActions() { id: TERMINAL_COMMAND_ID.SELECT_TO_NEXT_LINE, title: { value: localize('workbench.action.terminal.selectToNextLine', "Select To Next Line"), original: 'Select To Next Line' }, f1: true, - category + category, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -1089,7 +1120,8 @@ export function registerTerminalActions() { id: TERMINAL_COMMAND_ID.TOGGLE_ESCAPE_SEQUENCE_LOGGING, title: { value: localize('workbench.action.terminal.toggleEscapeSequenceLogging', "Toggle Escape Sequence Logging"), original: 'Toggle Escape Sequence Logging' }, f1: true, - category + category, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -1115,7 +1147,8 @@ export function registerTerminalActions() { }, } }] - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor, args?: { text?: string }) { @@ -1144,7 +1177,8 @@ export function registerTerminalActions() { }, } }] - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } async run(accessor: ServicesAccessor, args?: { cwd?: string }) { @@ -1180,7 +1214,8 @@ export function registerTerminalActions() { } } }] - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor, args?: { name?: string }) { @@ -1204,7 +1239,8 @@ export function registerTerminalActions() { mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R }, when: ContextKeyExpr.or(KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED), weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -1225,6 +1261,7 @@ export function registerTerminalActions() { when: ContextKeyExpr.or(KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED), weight: KeybindingWeight.WorkbenchContrib }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -1244,7 +1281,8 @@ export function registerTerminalActions() { mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C }, when: ContextKeyExpr.or(KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED), weight: KeybindingWeight.WorkbenchContrib - } + }, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -1271,7 +1309,8 @@ export function registerTerminalActions() { when: KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED, weight: KeybindingWeight.WorkbenchContrib } - ] + ], + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -1297,7 +1336,8 @@ export function registerTerminalActions() { when: KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED, weight: KeybindingWeight.WorkbenchContrib } - ] + ], + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -1310,7 +1350,8 @@ export function registerTerminalActions() { id: TERMINAL_COMMAND_ID.RELAUNCH, title: { value: localize('workbench.action.terminal.relaunch', "Relaunch Active Terminal"), original: 'Relaunch Active Terminal' }, f1: true, - category + category, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { @@ -1323,7 +1364,8 @@ export function registerTerminalActions() { id: TERMINAL_COMMAND_ID.SHOW_ENVIRONMENT_INFORMATION, title: { value: localize('workbench.action.terminal.showEnvironmentInformation', "Show Environment Information"), original: 'Show Environment Information' }, f1: true, - category + category, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } run(accessor: ServicesAccessor) { From 9b117bbb1dc729e9f53aa857e3ad0e043f2dcb1e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 27 Aug 2020 12:48:15 -0700 Subject: [PATCH 620/736] Move custom editor activation into mainThreadCustomEditors --- .../api/browser/mainThreadCustomEditors.ts | 18 ++++++++++++++++-- .../api/browser/mainThreadWebviewPanels.ts | 6 ------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts index 9bd40eeb40d..420344cb4b8 100644 --- a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts @@ -29,10 +29,12 @@ import { CustomDocumentBackupData } from 'vs/workbench/contrib/customEditor/brow import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { CustomTextEditorModel } from 'vs/workbench/contrib/customEditor/common/customTextEditorModel'; import { WebviewExtensionDescription } from 'vs/workbench/contrib/webview/browser/webview'; +import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; import { IWebviewWorkbenchService } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { IWorkingCopy, IWorkingCopyBackup, IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; @@ -51,6 +53,7 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc private readonly mainThreadWebview: MainThreadWebviews, private readonly mainThreadWebviewPanels: MainThreadWebviewPanels, context: extHostProtocol.IExtHostContext, + @IExtensionService extensionService: IExtensionService, @IWorkingCopyService workingCopyService: IWorkingCopyService, @IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService, @ICustomEditorService private readonly _customEditorService: ICustomEditorService, @@ -63,7 +66,7 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc this._proxyCustomEditors = context.getProxy(extHostProtocol.ExtHostContext.ExtHostCustomEditors); - workingCopyFileService.registerWorkingCopyProvider((editorResource) => { + this._register(workingCopyFileService.registerWorkingCopyProvider((editorResource) => { const matchedWorkingCopies: IWorkingCopy[] = []; for (const workingCopy of workingCopyService.workingCopies) { @@ -74,7 +77,18 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc } } return matchedWorkingCopies; - }); + })); + + // This reviver's only job is to activate custom editor extensions. + this._register(_webviewWorkbenchService.registerResolver({ + canResolve: (webview: WebviewInput) => { + if (webview instanceof CustomEditorInput) { + extensionService.activateByEvent(`onCustomEditor:${webview.viewType}`); + } + return false; + }, + resolveWebview: () => { throw new Error('not implemented'); } + })); } dispose() { diff --git a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts index b089b04b6dd..32e247e848a 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts @@ -12,7 +12,6 @@ import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; import { IEditorInput } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; -import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; import { WebviewIcons } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; import { ICreateWebViewShowOptions, IWebviewWorkbenchService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; @@ -116,11 +115,6 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc // This should trigger the real reviver to be registered from the extension host side. this._register(_webviewWorkbenchService.registerResolver({ canResolve: (webview: WebviewInput) => { - if (webview instanceof CustomEditorInput) { - extensionService.activateByEvent(`onCustomEditor:${webview.viewType}`); - return false; - } - const viewType = this.webviewPanelViewType.toExternal(webview.viewType); if (typeof viewType === 'string') { extensionService.activateByEvent(`onWebviewPanel:${viewType}`); From 08be66406f2d1d6d211cbfdc0c9850b29782fb92 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 27 Aug 2020 12:49:19 -0700 Subject: [PATCH 621/736] Make context the first arugment to constructors --- src/vs/workbench/api/browser/mainThreadCustomEditors.ts | 2 +- src/vs/workbench/api/browser/mainThreadWebviewManager.ts | 6 +++--- src/vs/workbench/api/browser/mainThreadWebviewPanels.ts | 2 +- src/vs/workbench/api/browser/mainThreadWebviewViews.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts index 420344cb4b8..9351d89b4bd 100644 --- a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts @@ -50,9 +50,9 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc private readonly _editorProviders = new Map(); constructor( + context: extHostProtocol.IExtHostContext, private readonly mainThreadWebview: MainThreadWebviews, private readonly mainThreadWebviewPanels: MainThreadWebviewPanels, - context: extHostProtocol.IExtHostContext, @IExtensionService extensionService: IExtensionService, @IWorkingCopyService workingCopyService: IWorkingCopyService, @IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService, diff --git a/src/vs/workbench/api/browser/mainThreadWebviewManager.ts b/src/vs/workbench/api/browser/mainThreadWebviewManager.ts index abe953dd010..225f15b104c 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewManager.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewManager.ts @@ -23,13 +23,13 @@ export class MainThreadWebviewManager extends Disposable { const webviews = this._register(instantiationService.createInstance(MainThreadWebviews, context)); context.set(extHostProtocol.MainContext.MainThreadWebviews, webviews); - const webviewPanels = this._register(instantiationService.createInstance(MainThreadWebviewPanels, webviews, context)); + const webviewPanels = this._register(instantiationService.createInstance(MainThreadWebviewPanels, context, webviews)); context.set(extHostProtocol.MainContext.MainThreadWebviewPanels, webviewPanels); - const customEditors = this._register(instantiationService.createInstance(MainThreadCustomEditors, webviews, webviewPanels, context)); + const customEditors = this._register(instantiationService.createInstance(MainThreadCustomEditors, context, webviews, webviewPanels)); context.set(extHostProtocol.MainContext.MainThreadCustomEditors, customEditors); - const webviewViews = this._register(instantiationService.createInstance(MainThreadWebviewsViews, webviews, context)); + const webviewViews = this._register(instantiationService.createInstance(MainThreadWebviewsViews, context, webviews)); context.set(extHostProtocol.MainContext.MainThreadWebviewViews, webviewViews); } } diff --git a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts index 32e247e848a..84107c980a3 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts @@ -86,8 +86,8 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc private readonly _revivers = new Map(); constructor( - private readonly _mainThreadWebviews: MainThreadWebviews, context: extHostProtocol.IExtHostContext, + private readonly _mainThreadWebviews: MainThreadWebviews, @IExtensionService extensionService: IExtensionService, @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, @IEditorService private readonly _editorService: IEditorService, diff --git a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts index df46e3c6e09..9b2ab1b996f 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts @@ -19,8 +19,8 @@ export class MainThreadWebviewsViews extends Disposable implements extHostProtoc private readonly _webviewViewProviders = new Map(); constructor( - private readonly mainThreadWebviews: MainThreadWebviews, context: extHostProtocol.IExtHostContext, + private readonly mainThreadWebviews: MainThreadWebviews, @IWebviewViewService private readonly _webviewViewService: IWebviewViewService, ) { super(); From f542ed32c118cbe1254ebb64766fc9938de91696 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 27 Aug 2020 12:49:47 -0700 Subject: [PATCH 622/736] Rename --- src/vs/workbench/api/browser/mainThreadWebviewViews.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts index 9b2ab1b996f..248af0860e6 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts @@ -13,7 +13,7 @@ import { IWebviewViewService, WebviewView } from 'vs/workbench/contrib/webviewVi export class MainThreadWebviewsViews extends Disposable implements extHostProtocol.MainThreadWebviewViewsShape { - private readonly _proxyViews: extHostProtocol.ExtHostWebviewViewsShape; + private readonly _proxy: extHostProtocol.ExtHostWebviewViewsShape; private readonly _webviewViews = new Map(); private readonly _webviewViewProviders = new Map(); @@ -25,7 +25,7 @@ export class MainThreadWebviewsViews extends Disposable implements extHostProtoc ) { super(); - this._proxyViews = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewViews); + this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewViews); } public $setWebviewViewTitle(handle: extHostProtocol.WebviewHandle, value: string | undefined): void { @@ -62,16 +62,16 @@ export class MainThreadWebviewsViews extends Disposable implements extHostProtoc } webviewView.onDidChangeVisibility(visible => { - this._proxyViews.$onDidChangeWebviewViewVisibility(handle, visible); + this._proxy.$onDidChangeWebviewViewVisibility(handle, visible); }); webviewView.onDispose(() => { - this._proxyViews.$disposeWebviewView(handle); + this._proxy.$disposeWebviewView(handle); this._webviewViews.delete(handle); }); try { - await this._proxyViews.$resolveWebviewView(handle, viewType, state, cancellation); + await this._proxy.$resolveWebviewView(handle, viewType, state, cancellation); } catch (error) { onUnexpectedError(error); webviewView.webview.html = this.mainThreadWebviews.getWebviewResolvedFailedContent(viewType); From 2579c033f267911af24689c6cb5bf5af970f9c95 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 27 Aug 2020 13:54:46 -0700 Subject: [PATCH 623/736] Enable pointer lock in webviews Fixes #104169 --- src/vs/workbench/contrib/webview/browser/pre/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index 8cd6211afde..a32d81eb75c 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -480,7 +480,7 @@ const newFrame = document.createElement('iframe'); newFrame.setAttribute('id', 'pending-frame'); newFrame.setAttribute('frameborder', '0'); - newFrame.setAttribute('sandbox', options.allowScripts ? 'allow-scripts allow-forms allow-same-origin' : 'allow-same-origin'); + newFrame.setAttribute('sandbox', options.allowScripts ? 'allow-scripts allow-forms allow-same-origin allow-pointer-lock' : 'allow-same-origin allow-pointer-lock'); if (host.fakeLoad) { // We should just be able to use srcdoc, but I wasn't // seeing the service worker applying properly. From 1c4f6acea57dfd19f98192157ea02eced7e0468b Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 27 Aug 2020 13:59:02 -0700 Subject: [PATCH 624/736] Gate webview polling usage to safari when scripts are disabled --- src/vs/workbench/contrib/webview/browser/pre/main.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index a32d81eb75c..41e39ecd9e2 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -19,6 +19,11 @@ (function () { 'use strict'; + const isSafari = navigator.vendor && navigator.vendor.indexOf('Apple') > -1 && + navigator.userAgent && + navigator.userAgent.indexOf('CriOS') === -1 && + navigator.userAgent.indexOf('FxiOS') === -1; + /** * Use polling to track focus of main webview and iframes within the webview * @@ -514,7 +519,7 @@ }, 0); } - if (host.fakeLoad && false) { + if (host.fakeLoad && !options.allowScripts && isSafari) { // On Safari for iframes with scripts disabled, the `DOMContentLoaded` never seems to be fired. // Use polling instead. const interval = setInterval(() => { @@ -524,7 +529,7 @@ return; } - if (newFrame.contentDocument.readyState === 'complete') { + if (newFrame.contentDocument.readyState !== 'loading') { clearInterval(interval); onFrameLoaded(newFrame.contentDocument); } From 2d1b28db22691754e409ec18a374264d48bf48e7 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 27 Aug 2020 14:37:16 -0700 Subject: [PATCH 625/736] Use the new lastCommittedOrigin for remote port forwarding in webviews (#105531) Fixes #102449 --- .../webview/common/webviewManagerService.ts | 2 +- .../electron-main/webviewMainService.ts | 4 +-- .../webviewPortMappingProvider.ts | 28 +++++++++---------- .../electron-browser/iframeWebviewElement.ts | 2 +- .../electron-browser/resourceLoading.ts | 17 +++++------ .../electron-browser/webviewElement.ts | 12 +------- 6 files changed, 25 insertions(+), 40 deletions(-) diff --git a/src/vs/platform/webview/common/webviewManagerService.ts b/src/vs/platform/webview/common/webviewManagerService.ts index 56171e56fc0..8963865f3b6 100644 --- a/src/vs/platform/webview/common/webviewManagerService.ts +++ b/src/vs/platform/webview/common/webviewManagerService.ts @@ -14,7 +14,7 @@ export const IWebviewManagerService = createDecorator('w export interface IWebviewManagerService { _serviceBrand: unknown; - registerWebview(id: string, webContentsId: number | undefined, windowId: number, metadata: RegisterWebviewMetadata): Promise; + registerWebview(id: string, windowId: number, metadata: RegisterWebviewMetadata): Promise; unregisterWebview(id: string): Promise; updateWebviewMetadata(id: string, metadataDelta: Partial): Promise; diff --git a/src/vs/platform/webview/electron-main/webviewMainService.ts b/src/vs/platform/webview/electron-main/webviewMainService.ts index 0e29b1cbaff..c3b49724aba 100644 --- a/src/vs/platform/webview/electron-main/webviewMainService.ts +++ b/src/vs/platform/webview/electron-main/webviewMainService.ts @@ -33,7 +33,7 @@ export class WebviewMainService extends Disposable implements IWebviewManagerSer this.portMappingProvider = this._register(new WebviewPortMappingProvider(tunnelService)); } - public async registerWebview(id: string, webContentsId: number | undefined, windowId: number, metadata: RegisterWebviewMetadata): Promise { + public async registerWebview(id: string, windowId: number, metadata: RegisterWebviewMetadata): Promise { const extensionLocation = metadata.extensionLocation ? URI.from(metadata.extensionLocation) : undefined; this.protocolProvider.registerWebview(id, { @@ -43,7 +43,7 @@ export class WebviewMainService extends Disposable implements IWebviewManagerSer localResourceRoots: metadata.localResourceRoots.map(x => URI.from(x)) }); - this.portMappingProvider.registerWebview(id, webContentsId, { + this.portMappingProvider.registerWebview(id, { extensionLocation, mappings: metadata.portMappings, resolvedAuthority: metadata.remoteConnectionData, diff --git a/src/vs/platform/webview/electron-main/webviewPortMappingProvider.ts b/src/vs/platform/webview/electron-main/webviewPortMappingProvider.ts index 94dc3036ebc..da60a2bc507 100644 --- a/src/vs/platform/webview/electron-main/webviewPortMappingProvider.ts +++ b/src/vs/platform/webview/electron-main/webviewPortMappingProvider.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { session } from 'electron'; +import { OnBeforeRequestListenerDetails, session } from 'electron'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IAddress } from 'vs/platform/remote/common/remoteAgentConnection'; @@ -11,6 +11,10 @@ import { ITunnelService } from 'vs/platform/remote/common/tunnel'; import { webviewPartitionId } from 'vs/platform/webview/common/resourceLoader'; import { IWebviewPortMapping, WebviewPortMappingManager } from 'vs/platform/webview/common/webviewPortMapping'; +interface OnBeforeRequestListenerDetails_Extended extends OnBeforeRequestListenerDetails { + readonly lastCommittedOrigin?: string; +} + interface PortMappingData { readonly extensionLocation: URI | undefined; readonly mappings: readonly IWebviewPortMapping[]; @@ -20,13 +24,10 @@ interface PortMappingData { export class WebviewPortMappingProvider extends Disposable { private readonly _webviewData = new Map(); - private _webContentsIdsToWebviewIds = new Map(); - constructor( @ITunnelService private readonly _tunnelService: ITunnelService, ) { @@ -40,12 +41,15 @@ export class WebviewPortMappingProvider extends Disposable { '*://127.0.0.1:*/*', '*://0.0.0.0:*/*', ] - }, async (details, callback) => { - const webviewId = details.webContentsId && this._webContentsIdsToWebviewIds.get(details.webContentsId); - if (!webviewId) { + }, async (details: OnBeforeRequestListenerDetails_Extended, callback) => { + let origin: URI; + try { + origin = URI.parse(details.lastCommittedOrigin!); + } catch { return callback({}); } + const webviewId = origin.authority; const entry = this._webviewData.get(webviewId); if (!entry) { return callback({}); @@ -56,16 +60,13 @@ export class WebviewPortMappingProvider extends Disposable { }); } - public async registerWebview(id: string, webContentsId: number | undefined, metadata: PortMappingData): Promise { + public async registerWebview(id: string, metadata: PortMappingData): Promise { const manager = new WebviewPortMappingManager( () => this._webviewData.get(id)?.metadata.extensionLocation, () => this._webviewData.get(id)?.metadata.mappings || [], this._tunnelService); - this._webviewData.set(id, { webContentsId, metadata, manager }); - if (typeof webContentsId === 'number') { - this._webContentsIdsToWebviewIds.set(webContentsId, id); - } + this._webviewData.set(id, { metadata, manager }); } public unregisterWebview(id: string): void { @@ -73,9 +74,6 @@ export class WebviewPortMappingProvider extends Disposable { if (existing) { existing.manager.dispose(); this._webviewData.delete(id); - if (typeof existing.webContentsId === 'number') { - this._webContentsIdsToWebviewIds.delete(existing.webContentsId); - } } } diff --git a/src/vs/workbench/contrib/webview/electron-browser/iframeWebviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/iframeWebviewElement.ts index 03d68b88996..9e776c2122d 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/iframeWebviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/iframeWebviewElement.ts @@ -52,7 +52,7 @@ export class ElectronIframeWebview extends IFrameWebview { super(id, options, contentOptions, extension, webviewThemeDataProvider, noficationService, tunnelService, fileService, requestService, telemetryService, environmentService, _workbenchEnvironmentService, _remoteAuthorityResolverService, logService); - this._resourceRequestManager = this._register(instantiationService.createInstance(WebviewResourceRequestManager, id, extension, this.content.options, Promise.resolve(undefined))); + this._resourceRequestManager = this._register(instantiationService.createInstance(WebviewResourceRequestManager, id, extension, this.content.options)); } protected createElement(options: WebviewOptions, contentOptions: WebviewContentOptions) { diff --git a/src/vs/workbench/contrib/webview/electron-browser/resourceLoading.ts b/src/vs/workbench/contrib/webview/electron-browser/resourceLoading.ts index e2d4a407857..4f62d6314fa 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/resourceLoading.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/resourceLoading.ts @@ -58,7 +58,6 @@ export class WebviewResourceRequestManager extends Disposable { private readonly id: string, private readonly extension: WebviewExtensionDescription | undefined, initialContentOptions: WebviewContentOptions, - getWebContentsId: Promise, @ILogService private readonly _logService: ILogService, @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService, @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @@ -79,15 +78,13 @@ export class WebviewResourceRequestManager extends Disposable { const remoteAuthority = environmentService.configuration.remoteAuthority; const remoteConnectionData = remoteAuthority ? remoteAuthorityResolverService.getConnectionData(remoteAuthority) : null; - this._ready = getWebContentsId.then(async (webContentsId) => { - this._logService.debug(`WebviewResourceRequestManager(${this.id}): did-start-loading`); - await this._webviewManagerService.registerWebview(this.id, webContentsId, electronService.windowId, { - extensionLocation: this.extension?.location.toJSON(), - localResourceRoots: this._localResourceRoots.map(x => x.toJSON()), - remoteConnectionData: remoteConnectionData, - portMappings: this._portMappings, - }); - + this._logService.debug(`WebviewResourceRequestManager(${this.id}): did-start-loading`); + this._ready = this._webviewManagerService.registerWebview(this.id, electronService.windowId, { + extensionLocation: this.extension?.location.toJSON(), + localResourceRoots: this._localResourceRoots.map(x => x.toJSON()), + remoteConnectionData: remoteConnectionData, + portMappings: this._portMappings, + }).then(() => { this._logService.debug(`WebviewResourceRequestManager(${this.id}): did register`); }); diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 2ef9b4d1bcf..0bf81242f3b 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -142,17 +142,7 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme this._myLogService.debug(`Webview(${this.id}): init`); - const webviewId = new Promise((resolve, reject) => { - const sub = this._register(addDisposableListener(this.element!, 'dom-ready', once(() => { - if (!this.element) { - reject(); - throw new Error('No element'); - } - resolve(this.element.getWebContentsId()); - sub.dispose(); - }))); - }); - this._resourceRequestManager = this._register(instantiationService.createInstance(WebviewResourceRequestManager, id, extension, this.content.options, webviewId)); + this._resourceRequestManager = this._register(instantiationService.createInstance(WebviewResourceRequestManager, id, extension, this.content.options)); this._register(addDisposableListener(this.element!, 'dom-ready', once(() => { this._register(ElectronWebviewBasedWebview.getWebviewKeyboardHandler(configurationService, mainProcessService).add(this.element!)); From ab04541f580b9c8564d98acaace40aa5d4725cbd Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 27 Aug 2020 14:56:17 -0700 Subject: [PATCH 626/736] Don't rerender outputs that were already rendered on metadata update and don't render new outputs when the cell output is collapsed Fix #104580 --- .../notebook/browser/notebookEditorWidget.ts | 46 +++++++++---------- .../browser/view/renderers/codeCell.ts | 15 ++++-- 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index df7797413d7..f12b346c3e6 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -6,11 +6,16 @@ import { getZoomLevel } from 'vs/base/browser/browser'; import * as DOM from 'vs/base/browser/dom'; import { IMouseWheelEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent'; +import { IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; +import { IAction, Separator } from 'vs/base/common/actions'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Color, RGBA } from 'vs/base/common/color'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { combinedDisposable, DisposableStore, Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { combinedDisposable, Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { ScrollEvent } from 'vs/base/common/scrollable'; +import { URI } from 'vs/base/common/uri'; +import { generateUuid } from 'vs/base/common/uuid'; import 'vs/css!./media/notebook'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; @@ -18,45 +23,40 @@ import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { Range } from 'vs/editor/common/core/range'; import { IEditor } from 'vs/editor/common/editorCommon'; import * as nls from 'vs/nls'; +import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { contrastBorder, editorBackground, focusBorder, foreground, registerColor, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground, textPreformatForeground, errorForeground, transparent, listFocusBackground, listInactiveSelectionBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, scrollbarSliderActiveBackground, diffRemoved, diffInserted } from 'vs/platform/theme/common/colorRegistry'; +import { contrastBorder, diffInserted, diffRemoved, editorBackground, errorForeground, focusBorder, foreground, listFocusBackground, listInactiveSelectionBackground, registerColor, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground, textPreformatForeground, transparent } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { EditorMemento } from 'vs/workbench/browser/parts/editor/editorPane'; import { IEditorMemento } from 'vs/workbench/common/editor'; -import { CELL_MARGIN, CELL_RUN_GUTTER, CELL_TOP_MARGIN, SCROLLABLE_ELEMENT_PADDING_TOP, BOTTOM_CELL_TOOLBAR_GAP, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, BOTTOM_CELL_TOOLBAR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellEditState, CellFocusMode, ICellRange, ICellViewModel, INotebookCellList, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_OUTPUT_FOCUSED, INotebookDeltaDecoration, NotebookEditorOptions, INotebookEditorCreationOptions, INotebookEditorContributionDescription } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { Memento, MementoObject } from 'vs/workbench/common/memento'; +import { PANEL_BORDER } from 'vs/workbench/common/theme'; +import { debugIconStartForeground } from 'vs/workbench/contrib/debug/browser/debugToolBar'; +import { BOTTOM_CELL_TOOLBAR_GAP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_MARGIN, CELL_RUN_GUTTER, CELL_TOP_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, SCROLLABLE_ELEMENT_PADDING_TOP } from 'vs/workbench/contrib/notebook/browser/constants'; +import { CellEditState, CellFocusMode, ICellRange, ICellViewModel, INotebookCellList, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorMouseEvent, NotebookEditorOptions, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; +import { NotebookKernelProviderAssociations, notebookKernelProviderAssociationsSettingId } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation'; import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList'; import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer'; import { BackLayerWebView } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView'; -import { CodeCellRenderer, MarkdownCellRenderer, NotebookCellListDelegate, ListTopCellToolbar } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer'; +import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys'; +import { CodeCellRenderer, ListTopCellToolbar, MarkdownCellRenderer, NotebookCellListDelegate } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer'; +import { CellDragAndDropController } from 'vs/workbench/contrib/notebook/browser/view/renderers/dnd'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { CellViewModel, IModelDecorationsChangeAccessor, INotebookEditorViewState, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { CellKind, IProcessedOutput, INotebookKernelInfo, INotebookKernelInfoDto, INotebookKernelInfo2, NotebookRunState, NotebookCellRunState, IInsetRenderOutput, CellToolbarLocKey, ShowCellStatusbarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; +import { CellKind, CellToolbarLocKey, IInsetRenderOutput, INotebookKernelInfo, INotebookKernelInfo2, INotebookKernelInfoDto, IProcessedOutput, NotebookCellRunState, NotebookRunState, ShowCellStatusbarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; +import { editorGutterModifiedBackground } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; -import { generateUuid } from 'vs/base/common/uuid'; -import { Memento, MementoObject } from 'vs/workbench/common/memento'; -import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { URI } from 'vs/base/common/uri'; -import { PANEL_BORDER } from 'vs/workbench/common/theme'; -import { debugIconStartForeground } from 'vs/workbench/contrib/debug/browser/debugToolBar'; -import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys'; -import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; -import { notebookKernelProviderAssociationsSettingId, NotebookKernelProviderAssociations } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation'; -import { ScrollEvent } from 'vs/base/common/scrollable'; -import { editorGutterModifiedBackground } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; -import { IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; -import { IAction, Separator } from 'vs/base/common/actions'; -import { CellDragAndDropController } from 'vs/workbench/contrib/notebook/browser/view/renderers/dnd'; const $ = DOM.$; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index 524f3326aaf..9ed49e93300 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -17,7 +17,7 @@ import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/ import { CellFocusMode, CodeCellRenderTemplate, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; -import { BUILTIN_RENDERER_ID, CellOutputKind, IProcessedOutput, IRenderOutput, ITransformedDisplayOutputDto, outputHasDynamicHeight, RenderOutputType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { BUILTIN_RENDERER_ID, CellOutputKind, IInsetRenderOutput, IProcessedOutput, IRenderOutput, ITransformedDisplayOutputDto, outputHasDynamicHeight, RenderOutputType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; interface IMimeTypeRenderer extends IQuickPickItem { @@ -196,7 +196,7 @@ export class CodeCell extends Disposable { // newly added element const currIndex = this.viewCell.outputs.indexOf(output); this.renderOutput(output, currIndex, prevElement); - prevElement = this.outputElements.get(output)!.element; + prevElement = this.outputElements.get(output)?.element; }); const editorHeight = templateData.editor!.getContentHeight(); @@ -326,13 +326,14 @@ export class CodeCell extends Disposable { const renderedOutput = this.outputElements.get(currOutput); if (renderedOutput) { if (renderedOutput.renderResult.type !== RenderOutputType.None) { - // Show inset in webview, or render output that isn't rendered - // TODO@roblou skipHeightInit flag is a hack - the webview only sends the real height once. Don't wipe it out here. - this.renderOutput(currOutput, index, undefined); + this.modifyInsetQueue = this.modifyInsetQueue.finally(() => this.notebookEditor.createInset(this.viewCell, renderedOutput.renderResult as IInsetRenderOutput, this.viewCell.getOutputOffset(index))); } else { // Anything else, just update the height this.viewCell.updateOutputHeight(index, renderedOutput.element.clientHeight); } + } else { + // Wasn't previously rendered, render it now + this.renderOutput(currOutput, index); } } @@ -438,6 +439,10 @@ export class CodeCell extends Disposable { } private renderOutput(currOutput: IProcessedOutput, index: number, beforeElement?: HTMLElement) { + if (this.viewCell.metadata.outputCollapsed) { + return; + } + if (!this.outputResizeListeners.has(currOutput)) { this.outputResizeListeners.set(currOutput, new DisposableStore()); } From a9204aa4b8b4bcde19704150ea7152643d43b0e7 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 27 Aug 2020 15:38:19 -0700 Subject: [PATCH 627/736] Don't hide switch terminal dropdown when proc not supported This was causing it not to show up when processes become supported later. Ideally the dropdown would be disabled like other action views but this will be done as part of #105543. --- src/vs/workbench/contrib/terminal/browser/terminalView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index c526770faa6..ae87e42d990 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -197,7 +197,7 @@ export class TerminalViewPane extends ViewPane { } public getActionViewItem(action: Action): IActionViewItem | undefined { - if (action.id === SwitchTerminalAction.ID && this._terminalService.isProcessSupportRegistered) { + if (action.id === SwitchTerminalAction.ID) { return this._instantiationService.createInstance(SwitchTerminalActionViewItem, action); } From 624fc1466ae478d5746cbb4f52763a7572220726 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 27 Aug 2020 15:53:29 -0700 Subject: [PATCH 628/736] Run codeql job every Tuesday Justification: - This was using _a lot_ of vm time and not giving us much - We only really need to run it once per release - Weekly as opposed to monthly means we can catch things earlier than when the release happens Fixes #105447 --- .github/workflows/codeql.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 5e990067f1d..017708844a4 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,6 +1,8 @@ name: "Code Scanning" -on: [push, pull_request] +on: + schedule: + - cron: '0 0 * * 2' jobs: CodeQL-Build: From 4d7ad58a7e9bbf8f672f7d0d1d586133a2b7fd3f Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 27 Aug 2020 16:38:24 -0700 Subject: [PATCH 629/736] Workaround for terminal shifting on space Fixes #104876 --- src/vs/workbench/contrib/terminal/browser/media/terminal.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 43de91e60fb..8c394244f15 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -196,3 +196,7 @@ padding: 0 22px 0 6px; } +/* HACK: Can remove when fixed upstream https://github.com/xtermjs/xterm.js/issues/3058 */ +.xterm-helper-textarea { + border: 0px; +} From b4b67b8e7eacafb504e545a05f31a60012df20e7 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 27 Aug 2020 16:55:10 -0700 Subject: [PATCH 630/736] re #102503. allow fold/unfold with levels and direction in notebook. --- .../notebook/browser/contrib/fold/folding.ts | 224 ++++++++++++------ .../browser/contrib/fold/foldingModel.ts | 71 +++++- 2 files changed, 224 insertions(+), 71 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts b/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts index 814dc3a2527..9d888af88b8 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts @@ -9,15 +9,15 @@ import * as DOM from 'vs/base/browser/dom'; import { CellFoldingState, FoldingModel } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel'; import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; -import { registerAction2, Action2 } from 'vs/platform/actions/common/actions'; +import { MenuRegistry, MenuId, ICommandAction } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { getActiveNotebookEditor, NOTEBOOK_ACTIONS_CATEGORY } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; import { localize } from 'vs/nls'; +import { FoldingRegion } from 'vs/editor/contrib/folding/foldingRanges'; export class FoldingController extends Disposable implements INotebookEditorContribution { static id: string = 'workbench.notebook.findController'; @@ -65,19 +65,31 @@ export class FoldingController extends Disposable implements INotebookEditorCont this._updateEditorFoldingRanges(); } - setFoldingState(index: number, state: CellFoldingState) { + setFoldingStateDown(index: number, state: CellFoldingState, levels: number) { + const doCollapse = state === CellFoldingState.Collapsed; + let region = this._foldingModel!.getRegionAtLine(index + 1); + let regions: FoldingRegion[] = []; + if (region) { + if (region.isCollapsed !== doCollapse) { + regions.push(region); + } + if (levels > 1) { + let regionsInside = this._foldingModel!.getRegionsInside(region, (r, level: number) => r.isCollapsed !== doCollapse && level < levels); + regions.push(...regionsInside); + } + } + + regions.forEach(r => this._foldingModel!.setCollapsed(r.regionIndex, state === CellFoldingState.Collapsed)); + this._updateEditorFoldingRanges(); + } + + setFoldingStateUp(index: number, state: CellFoldingState, levels: number) { if (!this._foldingModel) { return; } - const range = this._foldingModel.regions.findRange(index + 1); - const startIndex = this._foldingModel.regions.getStartLineNumber(range) - 1; - - if (startIndex !== index) { - return; - } - - this._foldingModel.setCollapsed(range, state === CellFoldingState.Collapsed); + let regions = this._foldingModel.getAllRegionsAtLine(index + 1, (region, level) => region.isCollapsed !== (state === CellFoldingState.Collapsed) && level <= levels); + regions.forEach(r => this._foldingModel!.setCollapsed(r.regionIndex, state === CellFoldingState.Collapsed)); this._updateEditorFoldingRanges(); } @@ -121,7 +133,7 @@ export class FoldingController extends Disposable implements INotebookEditorCont return; } - this.setFoldingState(modelIndex, state === CellFoldingState.Collapsed ? CellFoldingState.Expanded : CellFoldingState.Collapsed); + this.setFoldingStateUp(modelIndex, state === CellFoldingState.Collapsed ? CellFoldingState.Expanded : CellFoldingState.Collapsed, 1); } return; @@ -130,28 +142,47 @@ export class FoldingController extends Disposable implements INotebookEditorCont registerNotebookContribution(FoldingController.id, FoldingController); -registerAction2(class extends Action2 { - constructor() { - super({ - id: 'notebook.fold', - title: { value: localize('fold.cell', "Fold Cell"), original: 'Fold Cell' }, - category: NOTEBOOK_ACTIONS_CATEGORY, - keybinding: { - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_OPEN_SQUARE_BRACKET, - mac: { - primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_OPEN_SQUARE_BRACKET, - secondary: [KeyCode.LeftArrow], - }, - secondary: [KeyCode.LeftArrow], - weight: KeybindingWeight.WorkbenchContrib - }, - precondition: NOTEBOOK_IS_ACTIVE_EDITOR, - f1: true - }); - } +const NOTEBOOK_UNFOLD_COMMAND_ID = 'notebook.unfold'; +const NOTEBOOK_UNFOLD_COMMAND_LABEL = localize('unfold.cell', "Unfold Cell"); +const NOTEBOOK_FOLD_COMMAND_ID = 'notebook.fold'; +const NOTEBOOK_FOLD_COMMAND_LABEL = localize('fold.cell', "Fold Cell"); - async run(accessor: ServicesAccessor): Promise { +KeybindingsRegistry.registerCommandAndKeybindingRule({ + weight: KeybindingWeight.WorkbenchContrib, + when: null, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_CLOSE_SQUARE_BRACKET, + mac: { + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_CLOSE_SQUARE_BRACKET, + secondary: [KeyCode.RightArrow], + }, + secondary: [KeyCode.RightArrow], + id: NOTEBOOK_UNFOLD_COMMAND_ID, + description: { + description: NOTEBOOK_UNFOLD_COMMAND_LABEL, + args: [ + { + name: 'index', description: 'The cell index', schema: { + 'type': 'object', + 'required': ['index', 'direction'], + 'properties': { + 'index': { + 'type': 'number' + }, + 'direction': { + 'type': 'string', + 'enum': ['up', 'down'], + 'default': 'down' + }, + 'levels': { + 'type': 'number', + 'default': 1 + }, + } + } + } + ] + }, + handler: async (accessor, args?: { index: number, levels: number, direction: 'up' | 'down' }) => { const editorService = accessor.get(IEditorService); const editor = getActiveNotebookEditor(editorService); @@ -159,43 +190,78 @@ registerAction2(class extends Action2 { return; } - const activeCell = editor.getActiveCell(); - if (!activeCell) { - return; + const levels = args && args.levels || 1; + const direction = args && args.direction === 'up' ? 'up' : 'down'; + let index: number | undefined = undefined; + + if (args) { + index = args.index; + } else { + const activeCell = editor.getActiveCell(); + if (!activeCell) { + return; + } + index = editor.viewModel?.viewCells.indexOf(activeCell); } const controller = editor.getContribution(FoldingController.id); - - const index = editor.viewModel?.viewCells.indexOf(activeCell); - if (index !== undefined) { - controller.setFoldingState(index, CellFoldingState.Collapsed); + if (direction === 'up') { + controller.setFoldingStateUp(index, CellFoldingState.Expanded, levels); + } else { + controller.setFoldingStateDown(index, CellFoldingState.Expanded, levels); + } } } }); -registerAction2(class extends Action2 { - constructor() { - super({ - id: 'notebook.unfold', - title: { value: localize('unfold.cell', "Unfold Cell"), original: 'Unfold Cell' }, - category: NOTEBOOK_ACTIONS_CATEGORY, - keybinding: { - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_CLOSE_SQUARE_BRACKET, - mac: { - primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_CLOSE_SQUARE_BRACKET, - secondary: [KeyCode.RightArrow], - }, - secondary: [KeyCode.RightArrow], - weight: KeybindingWeight.WorkbenchContrib - }, - precondition: NOTEBOOK_IS_ACTIVE_EDITOR, - f1: true - }); - } +const unfoldCommand: ICommandAction = { + id: NOTEBOOK_UNFOLD_COMMAND_ID, + title: { value: NOTEBOOK_UNFOLD_COMMAND_LABEL, original: 'Unfold Cell' }, + category: NOTEBOOK_ACTIONS_CATEGORY, + precondition: NOTEBOOK_IS_ACTIVE_EDITOR +}; - async run(accessor: ServicesAccessor): Promise { +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: unfoldCommand, when: unfoldCommand.precondition }); +MenuRegistry.addCommand(unfoldCommand); + + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_OPEN_SQUARE_BRACKET, + mac: { + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_OPEN_SQUARE_BRACKET, + secondary: [KeyCode.LeftArrow], + }, + secondary: [KeyCode.LeftArrow], + weight: KeybindingWeight.WorkbenchContrib, + id: NOTEBOOK_FOLD_COMMAND_ID, + description: { + description: NOTEBOOK_FOLD_COMMAND_LABEL, + args: [ + { + name: 'index', description: 'The cell index', schema: { + 'type': 'object', + 'required': ['index', 'direction'], + 'properties': { + 'index': { + 'type': 'number' + }, + 'direction': { + 'type': 'string', + 'enum': ['up', 'down'], + 'default': 'down' + }, + 'levels': { + 'type': 'number', + 'default': 1 + }, + } + } + } + ] + }, + handler: async (accessor, args?: { index: number, levels: number, direction: 'up' | 'down' }) => { const editorService = accessor.get(IEditorService); const editor = getActiveNotebookEditor(editorService); @@ -203,17 +269,37 @@ registerAction2(class extends Action2 { return; } - const activeCell = editor.getActiveCell(); - if (!activeCell) { - return; + const levels = args && args.levels || 1; + const direction = args && args.direction === 'up' ? 'up' : 'down'; + let index: number | undefined = undefined; + + if (args) { + index = args.index; + } else { + const activeCell = editor.getActiveCell(); + if (!activeCell) { + return; + } + index = editor.viewModel?.viewCells.indexOf(activeCell); } const controller = editor.getContribution(FoldingController.id); - - const index = editor.viewModel?.viewCells.indexOf(activeCell); - if (index !== undefined) { - controller.setFoldingState(index, CellFoldingState.Expanded); + if (direction === 'up') { + controller.setFoldingStateUp(index, CellFoldingState.Collapsed, levels); + } else { + controller.setFoldingStateDown(index, CellFoldingState.Collapsed, levels); + } } } }); + +const foldCommand: ICommandAction = { + id: NOTEBOOK_FOLD_COMMAND_ID, + title: { value: NOTEBOOK_FOLD_COMMAND_LABEL, original: 'Fold Cell' }, + category: NOTEBOOK_ACTIONS_CATEGORY, + precondition: NOTEBOOK_IS_ACTIVE_EDITOR +}; + +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: foldCommand, when: foldCommand.precondition }); +MenuRegistry.addCommand(foldCommand); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts b/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts index 18f853b05e5..fade3589aa0 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts @@ -6,12 +6,16 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { TrackedRangeStickiness } from 'vs/editor/common/model'; -import { FoldingRegions } from 'vs/editor/contrib/folding/foldingRanges'; +import { FoldingRegion, FoldingRegions } from 'vs/editor/contrib/folding/foldingRanges'; import { IFoldingRangeData, sanitizeRanges } from 'vs/editor/contrib/folding/syntaxRangeProvider'; import { ICellRange } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +type RegionFilter = (r: FoldingRegion) => boolean; +type RegionFilterWithLevel = (r: FoldingRegion, level: number) => boolean; + + export class FoldingModel extends Disposable { private _viewModel: NotebookViewModel | null = null; private _viewModelStore = new DisposableStore(); @@ -73,7 +77,70 @@ export class FoldingModel extends Disposable { this.recompute(); } - public setCollapsed(index: number, newState: boolean) { + getRegionAtLine(lineNumber: number): FoldingRegion | null { + if (this._regions) { + let index = this._regions.findRange(lineNumber); + if (index >= 0) { + return this._regions.toRegion(index); + } + } + return null; + } + + getRegionsInside(region: FoldingRegion | null, filter?: RegionFilter | RegionFilterWithLevel): FoldingRegion[] { + let result: FoldingRegion[] = []; + let index = region ? region.regionIndex + 1 : 0; + let endLineNumber = region ? region.endLineNumber : Number.MAX_VALUE; + + if (filter && filter.length === 2) { + const levelStack: FoldingRegion[] = []; + for (let i = index, len = this._regions.length; i < len; i++) { + let current = this._regions.toRegion(i); + if (this._regions.getStartLineNumber(i) < endLineNumber) { + while (levelStack.length > 0 && !current.containedBy(levelStack[levelStack.length - 1])) { + levelStack.pop(); + } + levelStack.push(current); + if (filter(current, levelStack.length)) { + result.push(current); + } + } else { + break; + } + } + } else { + for (let i = index, len = this._regions.length; i < len; i++) { + let current = this._regions.toRegion(i); + if (this._regions.getStartLineNumber(i) < endLineNumber) { + if (!filter || (filter as RegionFilter)(current)) { + result.push(current); + } + } else { + break; + } + } + } + return result; + } + + getAllRegionsAtLine(lineNumber: number, filter?: (r: FoldingRegion, level: number) => boolean): FoldingRegion[] { + let result: FoldingRegion[] = []; + if (this._regions) { + let index = this._regions.findRange(lineNumber); + let level = 1; + while (index >= 0) { + let current = this._regions.toRegion(index); + if (!filter || filter(current, level)) { + result.push(current); + } + level++; + index = current.parentIndex; + } + } + return result; + } + + setCollapsed(index: number, newState: boolean) { this._regions.setCollapsed(index, newState); } From 1d7ff4c7181e16b87b88365a651ba8c8e326e547 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 27 Aug 2020 17:10:26 -0700 Subject: [PATCH 631/736] emit notebook editor selection change. --- src/vs/vscode.proposed.d.ts | 10 ++++++++-- src/vs/workbench/api/common/extHost.api.impl.ts | 4 ++++ src/vs/workbench/api/common/extHostNotebook.ts | 7 +++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 0d67860afe2..485789a4ffb 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1464,6 +1464,11 @@ declare module 'vscode' { readonly cell: NotebookCell; } + export interface NotebookEditorSelectionChangeEvent { + readonly notebookEditor: NotebookEditor; + readonly selection?: NotebookCell; + } + export interface NotebookCellData { readonly cellKind: CellKind; readonly source: string; @@ -1680,11 +1685,12 @@ declare module 'vscode' { */ export const notebookDocuments: ReadonlyArray; - export let visibleNotebookEditors: NotebookEditor[]; + export const visibleNotebookEditors: NotebookEditor[]; export const onDidChangeVisibleNotebookEditors: Event; - export let activeNotebookEditor: NotebookEditor | undefined; + export const activeNotebookEditor: NotebookEditor | undefined; export const onDidChangeActiveNotebookEditor: Event; + export const onDidChangeNotebookEditorSelection: Event; export const onDidChangeNotebookCells: Event; export const onDidChangeCellOutputs: Event; export const onDidChangeCellLanguage: Event; diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 1a7edca9370..d9943430885 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -977,6 +977,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension); return extHostNotebook.onDidChangeNotebookCells(listener, thisArgs, disposables); }, + onDidChangeNotebookEditorSelection(listener, thisArgs?, disposables?) { + checkProposedApiEnabled(extension); + return extHostNotebook.onDidChangeNotebookEditorSelection(listener, thisArgs, disposables); + }, onDidChangeCellOutputs(listener, thisArgs?, disposables?) { checkProposedApiEnabled(extension); return extHostNotebook.onDidChangeCellOutputs(listener, thisArgs, disposables); diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index c9b0f4dd564..ae6d7860f62 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -870,6 +870,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN private readonly _editors = new Map(); private readonly _webviewComm = new Map(); private readonly _commandsConverter: CommandsConverter; + private readonly _onDidChangeNotebookEditorSelection = new Emitter(); + readonly onDidChangeNotebookEditorSelection = this._onDidChangeNotebookEditorSelection.event; private readonly _onDidChangeNotebookCells = new Emitter(); readonly onDidChangeNotebookCells = this._onDidChangeNotebookCells.event; private readonly _onDidChangeCellOutputs = new Emitter(); @@ -1285,6 +1287,11 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } else { editor.editor.selection = undefined; } + + this._onDidChangeNotebookEditorSelection.fire({ + notebookEditor: editor.editor, + selection: editor.editor.selection + }); } if (data.metadata) { From ee7447781acf0754cb9ac79d9b21280c36d83f3e Mon Sep 17 00:00:00 2001 From: n-gist Date: Fri, 28 Aug 2020 09:41:51 +0600 Subject: [PATCH 632/736] Slight capturer simplification --- .../editor/contrib/suggest/suggestOvertypingCapturer.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/vs/editor/contrib/suggest/suggestOvertypingCapturer.ts b/src/vs/editor/contrib/suggest/suggestOvertypingCapturer.ts index e6231a7a95d..0a23165a34f 100644 --- a/src/vs/editor/contrib/suggest/suggestOvertypingCapturer.ts +++ b/src/vs/editor/contrib/suggest/suggestOvertypingCapturer.ts @@ -13,16 +13,14 @@ export class OvertypingCapturer implements IDisposable { private readonly _disposables = new DisposableStore(); private _lastOvertyped: string[]; - private _holdCurrent: boolean; private _empty: boolean; constructor(editor: ICodeEditor, suggestModel: SuggestModel) { this._lastOvertyped = new Array(0); - this._holdCurrent = false; this._empty = true; this._disposables.add(editor.onWillType(text => { - if (this._holdCurrent) { + if (!this._empty) { return; } const selections = editor.getSelections(); @@ -63,15 +61,10 @@ export class OvertypingCapturer implements IDisposable { this._empty = false; })); - this._disposables.add(suggestModel.onDidSuggest(e => { - this._holdCurrent = true; - })); - this._disposables.add(suggestModel.onDidCancel(e => { if (!this._empty) { this._empty = true; } - this._holdCurrent = false; })); } From b5f6a65bba382275f10dfebf24e9abff5b611c3d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 28 Aug 2020 08:53:27 +0200 Subject: [PATCH 633/736] mark storage service as optional undefined --- .../extensionManagement/common/extensionGalleryService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index 513ce8e92a7..fef3856422f 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -347,7 +347,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { @ITelemetryService private readonly telemetryService: ITelemetryService, @IFileService private readonly fileService: IFileService, @IProductService private readonly productService: IProductService, - @optional(IStorageService) storageService: IStorageService, + @optional(IStorageService) storageService: IStorageService | undefined, ) { const config = productService.extensionsGallery; this.extensionsGalleryUrl = config && config.serviceUrl; From 1cc26ce4269e317fcc0f661e8a3da983fbe2aa5e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 28 Aug 2020 08:54:39 +0200 Subject: [PATCH 634/736] mark storage service as optional undefined --- .../extensionManagement/common/extensionGalleryService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index fef3856422f..513ce8e92a7 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -347,7 +347,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { @ITelemetryService private readonly telemetryService: ITelemetryService, @IFileService private readonly fileService: IFileService, @IProductService private readonly productService: IProductService, - @optional(IStorageService) storageService: IStorageService | undefined, + @optional(IStorageService) storageService: IStorageService, ) { const config = productService.extensionsGallery; this.extensionsGalleryUrl = config && config.serviceUrl; From 72a7ece4b2ff29cfd868e7a67308cb4cedd70a9c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 28 Aug 2020 08:59:31 +0200 Subject: [PATCH 635/736] :lipstick: optional services --- src/vs/editor/contrib/find/findController.ts | 5 +---- src/vs/platform/telemetry/common/telemetryService.ts | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/find/findController.ts b/src/vs/editor/contrib/find/findController.ts index f364eb97380..7e452f6a6cd 100644 --- a/src/vs/editor/contrib/find/findController.ts +++ b/src/vs/editor/contrib/find/findController.ts @@ -20,7 +20,6 @@ import { MenuId } from 'vs/platform/actions/common/actions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { optional } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -371,7 +370,6 @@ export class CommonFindController extends Disposable implements IEditorContribut public async getGlobalBufferTerm(): Promise { if (this._editor.getOption(EditorOption.find).globalFindClipboard - && this._clipboardService && this._editor.hasModel() && !this._editor.getModel().isTooLargeForSyncing() ) { @@ -382,7 +380,6 @@ export class CommonFindController extends Disposable implements IEditorContribut public setGlobalBufferTerm(text: string): void { if (this._editor.getOption(EditorOption.find).globalFindClipboard - && this._clipboardService && this._editor.hasModel() && !this._editor.getModel().isTooLargeForSyncing() ) { @@ -406,7 +403,7 @@ export class FindController extends CommonFindController implements IFindControl @INotificationService private readonly _notificationService: INotificationService, @IStorageService _storageService: IStorageService, @IStorageKeysSyncRegistryService private readonly _storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, - @optional(IClipboardService) clipboardService: IClipboardService, + @IClipboardService clipboardService: IClipboardService, ) { super(editor, _contextKeyService, _storageService, clipboardService); this._widget = null; diff --git a/src/vs/platform/telemetry/common/telemetryService.ts b/src/vs/platform/telemetry/common/telemetryService.ts index 1e1c6fcb583..3a8d6feef57 100644 --- a/src/vs/platform/telemetry/common/telemetryService.ts +++ b/src/vs/platform/telemetry/common/telemetryService.ts @@ -89,7 +89,7 @@ export class TelemetryService implements ITelemetryService { } private _updateUserOptIn(): void { - const config = this._configurationService.getValue(TELEMETRY_SECTION_ID); + const config = this._configurationService?.getValue(TELEMETRY_SECTION_ID); this._userOptIn = config ? config.enableTelemetry : this._userOptIn; } From e995f1095805e726c505ebb6477f2b6030a28cc7 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 28 Aug 2020 09:10:28 +0200 Subject: [PATCH 636/736] Use DOM API (fixes #105505) --- .../welcome/overlay/browser/welcomeOverlay.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts b/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts index 33bdbf1ba9d..c1c19309f36 100644 --- a/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts +++ b/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts @@ -38,43 +38,43 @@ interface Key { const keys: Key[] = [ { id: 'explorer', - arrow: '←', + arrow: '←', label: localize('welcomeOverlay.explorer', "File explorer"), command: 'workbench.view.explorer' }, { id: 'search', - arrow: '←', + arrow: '←', label: localize('welcomeOverlay.search', "Search across files"), command: 'workbench.view.search' }, { id: 'git', - arrow: '←', + arrow: '←', label: localize('welcomeOverlay.git', "Source code management"), command: 'workbench.view.scm' }, { id: 'debug', - arrow: '←', + arrow: '←', label: localize('welcomeOverlay.debug', "Launch and debug"), command: 'workbench.view.debug' }, { id: 'extensions', - arrow: '←', + arrow: '←', label: localize('welcomeOverlay.extensions', "Manage extensions"), command: 'workbench.view.extensions' }, // { // id: 'watermark', - // arrow: '⤹', + // arrow: '⤹', // label: localize('welcomeOverlay.watermark', "Command Hints"), // withEditor: false // }, { id: 'problems', - arrow: '⤹', + arrow: '⤹', label: localize('welcomeOverlay.problems', "View errors and warnings"), command: 'workbench.actions.view.problems' }, @@ -85,20 +85,20 @@ const keys: Key[] = [ }, // { // id: 'openfile', - // arrow: '⤸', + // arrow: '⤸', // label: localize('welcomeOverlay.openfile', "File Properties"), // arrowLast: true, // withEditor: true // }, { id: 'commandPalette', - arrow: '↖', + arrow: '↖', label: localize('welcomeOverlay.commandPalette', "Find and run all commands"), command: ShowAllCommandsAction.ID }, { id: 'notifications', - arrow: '⤵', + arrow: '⤵', arrowLast: true, label: localize('welcomeOverlay.notifications', "Show notifications"), command: 'notifications.showList' @@ -186,7 +186,7 @@ class WelcomeOverlay extends Disposable { .forEach(({ id, arrow, label, command, arrowLast }) => { const div = dom.append(this._overlay, $(`.key.${id}`)); if (arrow && !arrowLast) { - dom.append(div, $('span.arrow')).innerHTML = arrow; + dom.append(div, $('span.arrow', {}, arrow)); } dom.append(div, $('span.label')).textContent = label; if (command) { @@ -196,7 +196,7 @@ class WelcomeOverlay extends Disposable { } } if (arrow && arrowLast) { - dom.append(div, $('span.arrow')).innerHTML = arrow; + dom.append(div, $('span.arrow', {}, arrow)); } }); } From f813f15c67bc817365607c9116ba3b54e24a303f Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 28 Aug 2020 09:27:08 +0200 Subject: [PATCH 637/736] #105562 do not use instantiation service --- .../common/extensionManagementServerService.ts | 8 +++++++- .../extensionManagementServerService.ts | 12 +++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts index dfee9ed2d2a..2fa59ee92f4 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts @@ -15,6 +15,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { WebExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/webExtensionManagementService'; import { IExtension } from 'vs/platform/extensions/common/extensions'; import { WebRemoteExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/remoteExtensionManagementService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IProductService } from 'vs/platform/product/common/productService'; export class ExtensionManagementServerService implements IExtensionManagementServerService { @@ -27,11 +30,14 @@ export class ExtensionManagementServerService implements IExtensionManagementSer constructor( @IRemoteAgentService remoteAgentService: IRemoteAgentService, @ILabelService labelService: ILabelService, + @IExtensionGalleryService galleryService: IExtensionGalleryService, + @IProductService productService: IProductService, + @IConfigurationService configurationService: IConfigurationService, @IInstantiationService instantiationService: IInstantiationService, ) { const remoteAgentConnection = remoteAgentService.getConnection(); if (remoteAgentConnection) { - const extensionManagementService = instantiationService.createInstance(WebRemoteExtensionManagementService, remoteAgentConnection!.getChannel('extensions')); + const extensionManagementService = new WebRemoteExtensionManagementService(remoteAgentConnection.getChannel('extensions'), galleryService, configurationService, productService); this.remoteExtensionManagementServer = { id: 'remote', extensionManagementService, diff --git a/src/vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService.ts b/src/vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService.ts index fb573f0618f..7aabfa3b995 100644 --- a/src/vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService.ts @@ -15,7 +15,10 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { DesktopRemoteExtensionManagementService } from 'vs/workbench/services/extensionManagement/electron-browser/remoteExtensionManagementService'; import { ILabelService } from 'vs/platform/label/common/label'; import { IExtension } from 'vs/platform/extensions/common/extensions'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ILogService } from 'vs/platform/log/common/log'; export class ExtensionManagementServerService implements IExtensionManagementServerService { @@ -28,16 +31,19 @@ export class ExtensionManagementServerService implements IExtensionManagementSer constructor( @ISharedProcessService sharedProcessService: ISharedProcessService, - @IInstantiationService instantiationService: IInstantiationService, @IRemoteAgentService remoteAgentService: IRemoteAgentService, @ILabelService labelService: ILabelService, + @IExtensionGalleryService galleryService: IExtensionGalleryService, + @IProductService productService: IProductService, + @IConfigurationService configurationService: IConfigurationService, + @ILogService logService: ILogService, ) { const localExtensionManagementService = new ExtensionManagementChannelClient(sharedProcessService.getChannel('extensions')); this._localExtensionManagementServer = { extensionManagementService: localExtensionManagementService, id: 'local', label: localize('local', "Local") }; const remoteAgentConnection = remoteAgentService.getConnection(); if (remoteAgentConnection) { - const extensionManagementService = instantiationService.createInstance(DesktopRemoteExtensionManagementService, remoteAgentConnection.getChannel('extensions'), this.localExtensionManagementServer); + const extensionManagementService = new DesktopRemoteExtensionManagementService(remoteAgentConnection.getChannel('extensions'), this.localExtensionManagementServer, logService, galleryService, configurationService, productService); this.remoteExtensionManagementServer = { id: 'remote', extensionManagementService, From bd2a40195f1c904a0cc0dd161371c0e3e3957052 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 28 Aug 2020 09:31:39 +0200 Subject: [PATCH 638/736] fix compilation --- .../extensions/test/electron-browser/extensionsActions.test.ts | 2 +- .../extensions/test/electron-browser/extensionsViews.test.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts index 8f260d90da0..e2f71dede95 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts @@ -101,7 +101,7 @@ async function setupTest() { instantiationService.stub(IExtensionManagementServerService, new class extends ExtensionManagementServerService { #localExtensionManagementServer: IExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', id: 'vscode-local' }; constructor() { - super(instantiationService.get(ISharedProcessService), instantiationService, instantiationService.get(IRemoteAgentService), instantiationService.get(ILabelService)); + super(instantiationService.get(ISharedProcessService), instantiationService.get(IRemoteAgentService), instantiationService.get(ILabelService), instantiationService.get(IExtensionGalleryService), instantiationService.get(IProductService), instantiationService.get(IConfigurationService), instantiationService.get(ILogService)); } get localExtensionManagementServer(): IExtensionManagementServer { return this.#localExtensionManagementServer; } set localExtensionManagementServer(server: IExtensionManagementServer) { } diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts index 6c077780df1..a6c10ab0fc2 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts @@ -45,6 +45,7 @@ import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKe import { IMenuService } from 'vs/platform/actions/common/actions'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; +import { IProductService } from 'vs/platform/product/common/productService'; suite('ExtensionsListView Tests', () => { @@ -103,7 +104,7 @@ suite('ExtensionsListView Tests', () => { instantiationService.stub(IExtensionManagementServerService, new class extends ExtensionManagementServerService { #localExtensionManagementServer: IExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', id: 'vscode-local' }; constructor() { - super(instantiationService.get(ISharedProcessService), instantiationService, instantiationService.get(IRemoteAgentService), instantiationService.get(ILabelService)); + super(instantiationService.get(ISharedProcessService), instantiationService.get(IRemoteAgentService), instantiationService.get(ILabelService), instantiationService.get(IExtensionGalleryService), instantiationService.get(IProductService), instantiationService.get(IConfigurationService), instantiationService.get(ILogService)); } get localExtensionManagementServer(): IExtensionManagementServer { return this.#localExtensionManagementServer; } set localExtensionManagementServer(server: IExtensionManagementServer) { } From 8ff399e94205a29468f2bbd5d7b02ec7b19d89c9 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 28 Aug 2020 09:44:18 +0200 Subject: [PATCH 639/736] fuzzy scorer - add an additional boost based on label length for prefix matches only --- src/vs/base/common/fuzzyScorer.ts | 9 +++++++++ src/vs/base/test/common/fuzzyScorer.test.ts | 15 +++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/vs/base/common/fuzzyScorer.ts b/src/vs/base/common/fuzzyScorer.ts index 12c3e4ad739..11fc4bc0ed3 100644 --- a/src/vs/base/common/fuzzyScorer.ts +++ b/src/vs/base/common/fuzzyScorer.ts @@ -463,6 +463,7 @@ function doScoreItemFuzzySingle(label: string, description: string | undefined, if (preferLabelMatches || !description) { const [labelScore, labelPositions] = scoreFuzzy(label, query.normalized, query.normalizedLowercase, fuzzy); if (labelScore) { + // If we have a prefix match on the label, we give a much // higher baseScore to elevate these matches over others // This ensures that typing a file name wins over results @@ -472,6 +473,14 @@ function doScoreItemFuzzySingle(label: string, description: string | undefined, let baseScore: number; if (labelPrefixMatch) { baseScore = LABEL_PREFIX_SCORE_THRESHOLD; + + // We give another boost to labels that are short, e.g. given + // files "window.ts" and "windowActions.ts" and a query of + // "window", we want "window.ts" to receive a higher score. + // As such we compute the percentage the query has within the + // label and add that to the baseScore. + const prefixLengthBoost = Math.round((query.normalized.length / label.length) * 100); + baseScore += prefixLengthBoost; } else { baseScore = LABEL_SCORE_THRESHOLD; } diff --git a/src/vs/base/test/common/fuzzyScorer.test.ts b/src/vs/base/test/common/fuzzyScorer.test.ts index 31f211e7ce7..035082ca5d1 100644 --- a/src/vs/base/test/common/fuzzyScorer.test.ts +++ b/src/vs/base/test/common/fuzzyScorer.test.ts @@ -1040,6 +1040,21 @@ suite('Fuzzy Scorer', () => { } }); + test('compareFilesByScore - boost shorter prefix match if multiple queries are used', function () { + const resourceA = URI.file('src/vs/workbench/browser/actions/windowActions.ts'); + const resourceB = URI.file('src/vs/workbench/electron-browser/window.ts'); + + for (const query of ['window browser', 'window.ts browser']) { + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceB); + assert.equal(res[1], resourceA); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceB); + assert.equal(res[1], resourceA); + } + }); + test('prepareQuery', () => { assert.equal(scorer.prepareQuery(' f*a ').normalized, 'fa'); assert.equal(scorer.prepareQuery('model Tester.ts').original, 'model Tester.ts'); From 03811f92c55b140b00b067522b73c85ec1ea3155 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 28 Aug 2020 09:35:06 +0200 Subject: [PATCH 640/736] always keep a reference to dirty notebook models, https://github.com/microsoft/vscode/issues/105283 --- .../notebookEditorModelResolverService.ts | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts index 11587e61242..2206d05e61e 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts @@ -7,9 +7,10 @@ import { createDecorator, IInstantiationService } from 'vs/platform/instantiatio import { URI } from 'vs/base/common/uri'; import { INotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookEditorModel'; -import { IReference, ReferenceCollection } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable, IReference, ReferenceCollection } from 'vs/base/common/lifecycle'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { ILogService } from 'vs/platform/log/common/log'; +import { Event } from 'vs/base/common/event'; export const INotebookEditorModelResolverService = createDecorator('INotebookModelResolverService'); @@ -70,15 +71,27 @@ export class NotebookModelResolverService implements INotebookEditorModelResolve async resolve(resource: URI, viewType?: string, editorId?: string | undefined): Promise> { const reference = this._data.acquire(resource.toString(), viewType, editorId); const model = await reference.object; + NotebookModelResolverService._autoReferenceDirtyModel(model, () => this._data.acquire(resource.toString(), viewType, editorId)); return { object: model, dispose() { reference.dispose(); } }; } + + private static _autoReferenceDirtyModel(model: INotebookEditorModel, ref: () => IDisposable) { + + const references = new DisposableStore(); + const listener = model.notebook.onDidChangeDirty(() => { + if (model.notebook.isDirty) { + references.add(ref()); + } else { + references.clear(); + } + }); + + Event.once(model.notebook.onWillDispose)(() => { + listener.dispose(); + references.dispose(); + }); + } } - -// notebookService.onDidAddDocument - -// resolve() - -// notebookService.onDidRemoveDocument ... From 787ba7519deb92a299521b23067c1ab6aa3eec50 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 28 Aug 2020 10:19:11 +0200 Subject: [PATCH 641/736] fuzzy score - add test for #99171 --- src/vs/base/test/common/fuzzyScorer.test.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/vs/base/test/common/fuzzyScorer.test.ts b/src/vs/base/test/common/fuzzyScorer.test.ts index 035082ca5d1..5aac1fb98b4 100644 --- a/src/vs/base/test/common/fuzzyScorer.test.ts +++ b/src/vs/base/test/common/fuzzyScorer.test.ts @@ -1055,6 +1055,21 @@ suite('Fuzzy Scorer', () => { } }); + test('compareFilesByScore - boost shorter prefix match if multiple queries are used (#99171)', function () { + const resourceA = URI.file('mesh_editor_lifetime_job.h'); + const resourceB = URI.file('lifetime_job.h'); + + for (const query of ['m life, life m']) { + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceB); + assert.equal(res[1], resourceA); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceB); + assert.equal(res[1], resourceA); + } + }); + test('prepareQuery', () => { assert.equal(scorer.prepareQuery(' f*a ').normalized, 'fa'); assert.equal(scorer.prepareQuery('model Tester.ts').original, 'model Tester.ts'); From a4688615f4dcfbcff96aa32eb0ae7ebe68db0eb4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 28 Aug 2020 10:27:57 +0200 Subject: [PATCH 642/736] :up: web-playground version --- resources/web/code-web.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/web/code-web.js b/resources/web/code-web.js index 74dd39e8cb8..ee7e53e87db 100644 --- a/resources/web/code-web.js +++ b/resources/web/code-web.js @@ -27,7 +27,7 @@ const BUILTIN_MARKETPLACE_EXTENSIONS_ROOT = path.join(APP_ROOT, '.build', 'built const WEB_DEV_EXTENSIONS_ROOT = path.join(APP_ROOT, '.build', 'builtInWebDevExtensions'); const WEB_MAIN = path.join(APP_ROOT, 'src', 'vs', 'code', 'browser', 'workbench', 'workbench-dev.html'); -const WEB_PLAYGROUND_VERSION = '0.0.2'; +const WEB_PLAYGROUND_VERSION = '0.0.3'; const args = minimist(process.argv, { boolean: [ From d838ac2a854099b773d589345b7ef00db4f4f244 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 28 Aug 2020 10:30:43 +0200 Subject: [PATCH 643/736] :up: web-playground version --- resources/web/code-web.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/web/code-web.js b/resources/web/code-web.js index ee7e53e87db..8818b5d9efd 100644 --- a/resources/web/code-web.js +++ b/resources/web/code-web.js @@ -27,7 +27,7 @@ const BUILTIN_MARKETPLACE_EXTENSIONS_ROOT = path.join(APP_ROOT, '.build', 'built const WEB_DEV_EXTENSIONS_ROOT = path.join(APP_ROOT, '.build', 'builtInWebDevExtensions'); const WEB_MAIN = path.join(APP_ROOT, 'src', 'vs', 'code', 'browser', 'workbench', 'workbench-dev.html'); -const WEB_PLAYGROUND_VERSION = '0.0.3'; +const WEB_PLAYGROUND_VERSION = '0.0.4'; const args = minimist(process.argv, { boolean: [ From a2b9a5fd0fafc0a94659c71b1ced37f6411b8fc3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 28 Aug 2020 10:32:46 +0200 Subject: [PATCH 644/736] make sure to open dirty notebooks, https://github.com/microsoft/vscode/issues/105283 --- .../notebook/browser/notebook.contribution.ts | 38 +++++++++++++++++++ .../notebook/browser/notebookServiceImpl.ts | 4 ++ .../notebook/common/notebookService.ts | 1 + 3 files changed, 43 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 09954d65527..828c1b46615 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -50,6 +50,8 @@ import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/com import { NotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/browser/notebookCellStatusBarServiceImpl'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { Event } from 'vs/base/common/event'; // Editor Contribution @@ -511,10 +513,46 @@ class RegisterSchemasContribution extends Disposable implements IWorkbenchContri } } +// makes sure that every dirty notebook gets an editor +class NotebookFileTracker implements IWorkbenchContribution { + + private readonly _dirtyListener: IDisposable; + + constructor( + @INotebookService private readonly _notebookService: INotebookService, + @IEditorService private readonly _editorService: IEditorService, + @IWorkingCopyService workingCopyService: IWorkingCopyService, + ) { + this._dirtyListener = Event.debounce(workingCopyService.onDidChangeDirty, () => { }, 100)(() => { + const inputs = this._createMissingNotebookEditors(); + this._editorService.openEditors(inputs); + }); + } + + dispose(): void { + this._dirtyListener.dispose(); + } + + private _createMissingNotebookEditors(): IResourceEditorInput[] { + const result: IResourceEditorInput[] = []; + + for (const notebook of this._notebookService.getNotebookTextModels()) { + if (notebook.isDirty && !this._editorService.isOpen({ resource: notebook.uri })) { + result.push({ + resource: notebook.uri, + options: { inactive: true, preserveFocus: true, pinned: true } + }); + } + } + return result; + } +} + const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchContributionsRegistry.registerWorkbenchContribution(NotebookContribution, LifecyclePhase.Starting); workbenchContributionsRegistry.registerWorkbenchContribution(CellContentProvider, LifecyclePhase.Starting); workbenchContributionsRegistry.registerWorkbenchContribution(RegisterSchemasContribution, LifecyclePhase.Starting); +workbenchContributionsRegistry.registerWorkbenchContribution(NotebookFileTracker, LifecyclePhase.Ready); registerSingleton(INotebookService, NotebookService); registerSingleton(INotebookEditorWorkerService, NotebookEditorWorkerServiceImpl); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index dba22f1d968..defd17ca549 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -706,6 +706,10 @@ export class NotebookService extends Disposable implements INotebookService, ICu return this._models.get(uri)?.model; } + getNotebookTextModels(): Iterable { + return Iterable.map(this._models.values(), data => data.model); + } + private async transformTextModelOutputs(textModel: NotebookTextModel) { for (let i = 0; i < textModel.cells.length; i++) { const cell = textModel.cells[i]; diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index c58730d0491..f9acd633254 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -60,6 +60,7 @@ export interface INotebookService { resolveNotebook(viewType: string, uri: URI, forceReload: boolean, editorId?: string, backupId?: string): Promise; getNotebookTextModel(uri: URI): NotebookTextModel | undefined; + getNotebookTextModels(): Iterable; executeNotebook(viewType: string, uri: URI, kernelId: string): Promise; executeNotebookCell(viewType: string, uri: URI, handle: number, kernelId: string): Promise; getContributedNotebookProviders(resource: URI): readonly NotebookProviderInfo[]; From dd85f62e65325be84c2da2a63943a3322c3fb6a2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 28 Aug 2020 10:44:48 +0200 Subject: [PATCH 645/736] Process explorer: use icon font instead of SVG (fix #102560) (#105411) --- .../processExplorer/media/collapsed.svg | 1 - .../processExplorer/media/expanded.svg | 1 - .../processExplorer/media/processExplorer.css | 7 ++-- .../processExplorer/processExplorerMain.ts | 35 ++++++++++++------- 4 files changed, 27 insertions(+), 17 deletions(-) delete mode 100644 src/vs/code/electron-sandbox/processExplorer/media/collapsed.svg delete mode 100644 src/vs/code/electron-sandbox/processExplorer/media/expanded.svg diff --git a/src/vs/code/electron-sandbox/processExplorer/media/collapsed.svg b/src/vs/code/electron-sandbox/processExplorer/media/collapsed.svg deleted file mode 100644 index 3a63808c358..00000000000 --- a/src/vs/code/electron-sandbox/processExplorer/media/collapsed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/code/electron-sandbox/processExplorer/media/expanded.svg b/src/vs/code/electron-sandbox/processExplorer/media/expanded.svg deleted file mode 100644 index 75f73adbb02..00000000000 --- a/src/vs/code/electron-sandbox/processExplorer/media/expanded.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/code/electron-sandbox/processExplorer/media/processExplorer.css b/src/vs/code/electron-sandbox/processExplorer/media/processExplorer.css index fbb637e83cd..add9cdae262 100644 --- a/src/vs/code/electron-sandbox/processExplorer/media/processExplorer.css +++ b/src/vs/code/electron-sandbox/processExplorer/media/processExplorer.css @@ -58,6 +58,7 @@ table { width: 100%; table-layout: fixed; } + th[scope='col'] { vertical-align: bottom; border-bottom: 1px solid #cccccc; @@ -65,6 +66,7 @@ th[scope='col'] { border-top: 1px solid #cccccc; cursor: default; } + td { padding: .25rem; vertical-align: top; @@ -100,7 +102,6 @@ tbody > tr:hover { display: none; } -img { - width: 16px; - margin-right: 4px; +.header { + display: flex; } diff --git a/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts b/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts index b921e461ca9..1abe745c1d0 100644 --- a/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts +++ b/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/processExplorer'; +import 'vs/base/browser/ui/codicons/codiconStyles'; // make sure codicon css is loaded import { ElectronService, IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { localize } from 'vs/nls'; @@ -16,6 +17,7 @@ import { addDisposableListener, addClass } from 'vs/base/browser/dom'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { isRemoteDiagnosticError, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics'; import { MainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; +import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel'; const DEBUG_FLAGS_PATTERN = /\s--(inspect|debug)(-brk|port)?=(\d+)?/; const DEBUG_PORT_PATTERN = /\s--(inspect|debug)-port=(\d+)/; @@ -156,15 +158,15 @@ class ProcessExplorer { return maxProcessId; } - private updateSectionCollapsedState(shouldExpand: boolean, body: HTMLElement, twistie: HTMLImageElement, sectionName: string) { + private updateSectionCollapsedState(shouldExpand: boolean, body: HTMLElement, twistie: CodiconLabel, sectionName: string) { if (shouldExpand) { body.classList.remove('hidden'); this.collapsedStateCache.set(sectionName, false); - twistie.src = './media/expanded.svg'; + twistie.text = '$(chevron-down)'; } else { body.classList.add('hidden'); this.collapsedStateCache.set(sectionName, true); - twistie.src = './media/collapsed.svg'; + twistie.text = '$(chevron-right)'; } } @@ -191,18 +193,27 @@ class ProcessExplorer { private renderProcessGroupHeader(sectionName: string, body: HTMLElement, container: HTMLElement) { const headerRow = document.createElement('tr'); - const data = document.createElement('td'); - data.textContent = sectionName; - data.colSpan = 4; - headerRow.appendChild(data); - const twistie = document.createElement('img'); - this.updateSectionCollapsedState(!this.collapsedStateCache.get(sectionName), body, twistie, sectionName); - data.prepend(twistie); + const headerData = document.createElement('td'); + headerData.colSpan = 4; + headerRow.appendChild(headerData); - this.listeners.add(addDisposableListener(data, 'click', (e) => { + const headerContainer = document.createElement('div'); + headerContainer.className = 'header'; + headerData.appendChild(headerContainer); + + const twistieContainer = document.createElement('div'); + const twistieCodicon = new CodiconLabel(twistieContainer); + this.updateSectionCollapsedState(!this.collapsedStateCache.get(sectionName), body, twistieCodicon, sectionName); + headerContainer.appendChild(twistieContainer); + + const headerLabel = document.createElement('span'); + headerLabel.textContent = sectionName; + headerContainer.appendChild(headerLabel); + + this.listeners.add(addDisposableListener(headerData, 'click', (e) => { const isHidden = body.classList.contains('hidden'); - this.updateSectionCollapsedState(isHidden, body, twistie, sectionName); + this.updateSectionCollapsedState(isHidden, body, twistieCodicon, sectionName); })); container.appendChild(headerRow); From d22b4b7458a1fb87e259815ea13b3281be797e74 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 28 Aug 2020 11:29:35 +0200 Subject: [PATCH 646/736] know if overtyped text was multiline, save one iteration over selections, init arrays via [] --- .../contrib/snippet/snippetVariables.ts | 11 ++++--- .../suggest/suggestOvertypingCapturer.ts | 32 +++++++------------ 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/vs/editor/contrib/snippet/snippetVariables.ts b/src/vs/editor/contrib/snippet/snippetVariables.ts index 98381d6093c..cd5d505bcc9 100644 --- a/src/vs/editor/contrib/snippet/snippetVariables.ts +++ b/src/vs/editor/contrib/snippet/snippetVariables.ts @@ -85,15 +85,18 @@ export class SelectionBasedVariableResolver implements VariableResolver { if (name === 'SELECTION' || name === 'TM_SELECTED_TEXT') { let value = this._model.getValueInRange(this._selection) || undefined; + let isMultiline = this._selection.startLineNumber !== this._selection.endLineNumber; // If there was no selected text, try to get last overtyped text - let overtyped = false; if (!value && this._overtypingCapturer) { - value = this._overtypingCapturer.getLastOvertypedText(this._selectionIdx); - overtyped = true; + const info = this._overtypingCapturer.getLastOvertypedInfo(this._selectionIdx); + if (info) { + value = info.value; + isMultiline = info.multiline; + } } - if (value && (overtyped || this._selection.startLineNumber !== this._selection.endLineNumber) && variable.snippet) { + if (value && isMultiline && variable.snippet) { // Selection is a multiline string which we indentation we now // need to adjust. We compare the indentation of this variable // with the indentation at the editor position and add potential diff --git a/src/vs/editor/contrib/suggest/suggestOvertypingCapturer.ts b/src/vs/editor/contrib/suggest/suggestOvertypingCapturer.ts index 0a23165a34f..44eec0ca874 100644 --- a/src/vs/editor/contrib/suggest/suggestOvertypingCapturer.ts +++ b/src/vs/editor/contrib/suggest/suggestOvertypingCapturer.ts @@ -9,25 +9,23 @@ import { SuggestModel } from 'vs/editor/contrib/suggest/suggestModel'; export class OvertypingCapturer implements IDisposable { - private readonly _maxSelectionLength = 51200; + private static readonly _maxSelectionLength = 51200; private readonly _disposables = new DisposableStore(); - private _lastOvertyped: string[]; - private _empty: boolean; + private _lastOvertyped: { value: string; multiline: boolean }[] = []; + private _empty: boolean = true; constructor(editor: ICodeEditor, suggestModel: SuggestModel) { - this._lastOvertyped = new Array(0); - this._empty = true; - this._disposables.add(editor.onWillType(text => { + this._disposables.add(editor.onWillType(() => { if (!this._empty) { return; } - const selections = editor.getSelections(); - if (!selections) { + if (!editor.hasModel()) { return; } + const selections = editor.getSelections(); const selectionsLength = selections.length; // Check if it will overtype any selections @@ -42,21 +40,15 @@ export class OvertypingCapturer implements IDisposable { return; } + this._lastOvertyped = []; const model = editor.getModel(); - if (!model) { - return; - } - - // Check for overtyping capturer restrictions for (let i = 0; i < selectionsLength; i++) { - if (model.getValueLengthInRange(selections[i]) > this._maxSelectionLength) { + const selection = selections[i]; + // Check for overtyping capturer restrictions + if (model.getValueLengthInRange(selection) > OvertypingCapturer._maxSelectionLength) { return; } - } - - this._lastOvertyped = new Array(selectionsLength); - for (let i = 0; i < selectionsLength; i++) { - this._lastOvertyped[i] = model.getValueInRange(selections[i]); + this._lastOvertyped[i] = { value: model.getValueInRange(selection), multiline: selection.startLineNumber !== selection.endLineNumber }; } this._empty = false; })); @@ -68,7 +60,7 @@ export class OvertypingCapturer implements IDisposable { })); } - getLastOvertypedText(idx: number): string | undefined { + getLastOvertypedInfo(idx: number): { value: string; multiline: boolean } | undefined { if (!this._empty && idx >= 0 && idx < this._lastOvertyped.length) { return this._lastOvertyped[idx]; } From e21986eeaedc25d2d448819e64d6f87aab31309a Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Fri, 28 Aug 2020 11:31:38 +0200 Subject: [PATCH 647/736] add two more DAP stand-in types; see #70377 --- src/vs/vscode.proposed.d.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 485789a4ffb..32bf845aa31 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -738,6 +738,22 @@ declare module 'vscode' { //#region debug + /** + * A DebugProtocolVariableContainer is an opaque stand-in type for the intersection of the Scope and Variable types defined in the Debug Adapter Protocol. + * See https://microsoft.github.io/debug-adapter-protocol/specification#Types_Scope and https://microsoft.github.io/debug-adapter-protocol/specification#Types_Variable. + */ + export interface DebugProtocolVariableContainer { + // Properties: the intersection of DAP's Scope and Variable types. + } + + /** + * A DebugProtocolVariable is an opaque stand-in type for the Variable type defined in the Debug Adapter Protocol. + * See https://microsoft.github.io/debug-adapter-protocol/specification#Types_Variable. + */ + export interface DebugProtocolVariable { + // Properties: see details [here](https://microsoft.github.io/debug-adapter-protocol/specification#Base_Protocol_Variable). + } + // deprecated debug API export interface DebugConfigurationProvider { From 9056a337662375fe37ea960f09ced5cb12c57853 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 28 Aug 2020 11:41:53 +0200 Subject: [PATCH 648/736] sandbox - lift some desktop specific interfaces to common --- .../platform/menubar/electron-main/menubar.ts | 14 +++++---- src/vs/platform/windows/common/windows.ts | 28 ++++++++++++++++++ .../electron-main/windowsMainService.ts | 4 +-- src/vs/platform/windows/node/window.ts | 29 ++----------------- .../terminalNativeContribution.ts | 7 ++--- src/vs/workbench/electron-browser/window.ts | 9 +++--- 6 files changed, 47 insertions(+), 44 deletions(-) diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index e4cd33ab6e8..cfdb43bf474 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -7,15 +7,15 @@ import * as nls from 'vs/nls'; import { isMacintosh, language } from 'vs/base/common/platform'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { app, shell, Menu, MenuItem, BrowserWindow, MenuItemConstructorOptions, WebContents, Event, KeyboardEvent } from 'electron'; -import { getTitleBarStyle, IWindowOpenable } from 'vs/platform/windows/common/windows'; -import { OpenContext, IRunActionInWindowRequest, IRunKeybindingInWindowRequest } from 'vs/platform/windows/node/window'; +import { getTitleBarStyle, IDesktopRunActionInWindowRequest, IDesktopRunKeybindingInWindowRequest, IWindowOpenable } from 'vs/platform/windows/common/windows'; +import { OpenContext } from 'vs/platform/windows/node/window'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUpdateService, StateType } from 'vs/platform/update/common/update'; import product from 'vs/platform/product/common/product'; import { RunOnceScheduler } from 'vs/base/common/async'; import { ILogService } from 'vs/platform/log/common/log'; -import { mnemonicMenuLabel as baseMnemonicLabel } from 'vs/base/common/labels'; +import { mnemonicMenuLabel } from 'vs/base/common/labels'; import { IWindowsMainService, IWindowsCountChangedEvent } from 'vs/platform/windows/electron-main/windows'; import { IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService'; import { IMenubarData, IMenubarKeybinding, MenubarMenuItem, isMenubarMenuItemSeparator, isMenubarMenuItemSubmenu, isMenubarMenuItemAction, IMenubarMenu, isMenubarMenuItemUriAction } from 'vs/platform/menubar/common/menubar'; @@ -754,9 +754,11 @@ export class Menubar { } if (invocation.type === 'commandId') { - activeWindow.sendWhenReady('vscode:runAction', { id: invocation.commandId, from: 'menu' } as IRunActionInWindowRequest); + const runActionPayload: IDesktopRunActionInWindowRequest = { id: invocation.commandId, from: 'menu' }; + activeWindow.sendWhenReady('vscode:runAction', runActionPayload); } else { - activeWindow.sendWhenReady('vscode:runKeybinding', { userSettingsLabel: invocation.userSettingsLabel } as IRunKeybindingInWindowRequest); + const runKeybindingPayload: IDesktopRunKeybindingInWindowRequest = { userSettingsLabel: invocation.userSettingsLabel }; + activeWindow.sendWhenReady('vscode:runKeybinding', runKeybindingPayload); } } else { this.logService.trace('menubar#runActionInRenderer: no active window found', invocation); @@ -821,7 +823,7 @@ export class Menubar { } private mnemonicLabel(label: string): string { - return baseMnemonicLabel(label, !this.currentEnableMenuBarMnemonics); + return mnemonicMenuLabel(label, !this.currentEnableMenuBarMnemonics); } } diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index c6bd8a1c5d9..0bf273f83f7 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -170,11 +170,39 @@ export interface IPathData { overrideId?: string; } +export interface IPathsToWaitFor extends IPathsToWaitForData { + paths: IPath[]; + waitMarkerFileUri: URI; +} + +interface IPathsToWaitForData { + paths: IPathData[]; + waitMarkerFileUri: UriComponents; +} + export interface IOpenFileRequest { filesToOpenOrCreate?: IPathData[]; filesToDiff?: IPathData[]; } +/** + * Additional context for the request on desktop only. + */ +export interface IDesktopOpenFileRequest extends IOpenFileRequest { + termProgram?: string; + filesToWait?: IPathsToWaitForData; +} + +export interface IDesktopRunActionInWindowRequest { + id: string; + from: 'menu' | 'touchbar' | 'mouse'; + args?: any[]; +} + +export interface IDesktopRunKeybindingInWindowRequest { + userSettingsLabel: string; +} + export interface IWindowConfiguration { sessionId: string; diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 5005fe95c8b..d594c6d8cbb 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -19,8 +19,8 @@ import { screen, BrowserWindow, MessageBoxOptions, Display, app, nativeTheme } f import { ILifecycleMainService, UnloadReason, LifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; -import { IWindowSettings, IPath, isFileToOpen, isWorkspaceToOpen, isFolderToOpen, IWindowOpenable, IOpenEmptyWindowOptions, IAddFoldersRequest } from 'vs/platform/windows/common/windows'; -import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderUri, INativeWindowConfiguration, OpenContext, IPathsToWaitFor } from 'vs/platform/windows/node/window'; +import { IWindowSettings, IPath, isFileToOpen, isWorkspaceToOpen, isFolderToOpen, IWindowOpenable, IOpenEmptyWindowOptions, IAddFoldersRequest, IPathsToWaitFor } from 'vs/platform/windows/common/windows'; +import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderUri, INativeWindowConfiguration, OpenContext } from 'vs/platform/windows/node/window'; import { Emitter } from 'vs/base/common/event'; import product from 'vs/platform/product/common/product'; import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent, ICodeWindow, IWindowState as ISingleWindowState, WindowMode, IOpenEmptyConfiguration } from 'vs/platform/windows/electron-main/windows'; diff --git a/src/vs/platform/windows/node/window.ts b/src/vs/platform/windows/node/window.ts index 7d643a5963c..9adc39431df 100644 --- a/src/vs/platform/windows/node/window.ts +++ b/src/vs/platform/windows/node/window.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWindowConfiguration, IPath, IOpenFileRequest, IPathData } from 'vs/platform/windows/common/windows'; -import { URI, UriComponents } from 'vs/base/common/uri'; +import { IWindowConfiguration, IPathsToWaitFor } from 'vs/platform/windows/common/windows'; +import { URI } from 'vs/base/common/uri'; import * as platform from 'vs/base/common/platform'; import * as extpath from 'vs/base/common/extpath'; import { IWorkspaceIdentifier, IResolvedWorkspace, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; @@ -34,16 +34,6 @@ export const enum OpenContext { API } -export interface IRunActionInWindowRequest { - id: string; - from: 'menu' | 'touchbar' | 'mouse'; - args?: any[]; -} - -export interface IRunKeybindingInWindowRequest { - userSettingsLabel: string; -} - export interface INativeWindowConfiguration extends IWindowConfiguration, ParsedArgs { mainPid: number; @@ -72,21 +62,6 @@ export interface INativeWindowConfiguration extends IWindowConfiguration, Parsed filesToWait?: IPathsToWaitFor; } -export interface INativeOpenFileRequest extends IOpenFileRequest { - termProgram?: string; - filesToWait?: IPathsToWaitForData; -} - -export interface IPathsToWaitFor extends IPathsToWaitForData { - paths: IPath[]; - waitMarkerFileUri: URI; -} - -export interface IPathsToWaitForData { - paths: IPathData[]; - waitMarkerFileUri: UriComponents; -} - export interface IWindowContext { openedWorkspace?: IWorkspaceIdentifier; openedFolderUri?: URI; diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeContribution.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeContribution.ts index 52d755f78b2..11279095fcc 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeContribution.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeContribution.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; -import { IOpenFileRequest } from 'vs/platform/windows/common/windows'; +import { IDesktopOpenFileRequest } from 'vs/platform/windows/common/windows'; import { URI } from 'vs/base/common/uri'; import { IFileService } from 'vs/platform/files/common/files'; import { getWindowsBuildNumber, linuxDistro } from 'vs/workbench/contrib/terminal/node/terminal'; @@ -15,7 +15,6 @@ import { registerRemoteContributions } from 'vs/workbench/contrib/terminal/elect import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { Disposable } from 'vs/base/common/lifecycle'; -import { INativeOpenFileRequest } from 'vs/platform/windows/node/window'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -31,7 +30,7 @@ export class TerminalNativeContribution extends Disposable implements IWorkbench ) { super(); - ipcRenderer.on('vscode:openFiles', (_: unknown, request: IOpenFileRequest) => this._onOpenFileRequest(request)); + ipcRenderer.on('vscode:openFiles', (_: unknown, request: IDesktopOpenFileRequest) => this._onOpenFileRequest(request)); this._register(electronService.onOSResume(() => this._onOsResume())); this._terminalService.setLinuxDistro(linuxDistro); @@ -54,7 +53,7 @@ export class TerminalNativeContribution extends Disposable implements IWorkbench activeTab.terminalInstances.forEach(instance => instance.forceRedraw()); } - private async _onOpenFileRequest(request: INativeOpenFileRequest): Promise { + private async _onOpenFileRequest(request: IDesktopOpenFileRequest): Promise { // if the request to open files is coming in from the integrated terminal (identified though // the termProgram variable) and we are instructed to wait for editors close, wait for the // marker file to get deleted and then focus back to the integrated terminal. diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 3749645b62c..1c2fa2b665f 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -13,8 +13,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { toResource, IUntitledTextResourceEditorInput, SideBySideEditor, pathsToEditors } from 'vs/workbench/common/editor'; import { IEditorService, IResourceEditorInputType } from 'vs/workbench/services/editor/common/editorService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IWindowSettings, IOpenFileRequest, IWindowsConfiguration, getTitleBarStyle, IAddFoldersRequest } from 'vs/platform/windows/common/windows'; -import { IRunActionInWindowRequest, IRunKeybindingInWindowRequest, INativeOpenFileRequest } from 'vs/platform/windows/node/window'; +import { IWindowSettings, IOpenFileRequest, IWindowsConfiguration, getTitleBarStyle, IAddFoldersRequest, IDesktopRunActionInWindowRequest, IDesktopRunKeybindingInWindowRequest, IDesktopOpenFileRequest } from 'vs/platform/windows/common/windows'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { IWorkbenchThemeService, VS_HC_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { applyZoom } from 'vs/platform/windows/electron-sandbox/window'; @@ -129,7 +128,7 @@ export class NativeWindow extends Disposable { }); // Support runAction event - ipcRenderer.on('vscode:runAction', async (event: unknown, request: IRunActionInWindowRequest) => { + ipcRenderer.on('vscode:runAction', async (event: unknown, request: IDesktopRunActionInWindowRequest) => { const args: unknown[] = request.args || []; // If we run an action from the touchbar, we fill in the currently active resource @@ -160,7 +159,7 @@ export class NativeWindow extends Disposable { }); // Support runKeybinding event - ipcRenderer.on('vscode:runKeybinding', (event: unknown, request: IRunKeybindingInWindowRequest) => { + ipcRenderer.on('vscode:runKeybinding', (event: unknown, request: IDesktopRunKeybindingInWindowRequest) => { if (document.activeElement) { this.keybindingService.dispatchByUserSettingsLabel(request.userSettingsLabel, document.activeElement); } @@ -554,7 +553,7 @@ export class NativeWindow extends Disposable { this.workspaceEditingService.addFolders(foldersToAdd); } - private async onOpenFiles(request: INativeOpenFileRequest): Promise { + private async onOpenFiles(request: IDesktopOpenFileRequest): Promise { const inputs: IResourceEditorInputType[] = []; const diffMode = !!(request.filesToDiff && (request.filesToDiff.length === 2)); From 70e490fc292f6f42e3904c6fe30f041dadd6af4c Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 28 Aug 2020 12:12:53 +0200 Subject: [PATCH 649/736] debugUx context key: make sure to be simple if there are no debuggers --- src/vs/workbench/contrib/debug/browser/debugService.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 898d4f0ab82..7f5e9a6e2a0 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -111,7 +111,7 @@ export class DebugService implements IDebugService { this.debugState = CONTEXT_DEBUG_STATE.bindTo(contextKeyService); this.inDebugMode = CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService); this.debugUx = CONTEXT_DEBUG_UX.bindTo(contextKeyService); - this.debugUx.set(!!this.configurationManager.selectedConfiguration.name ? 'default' : 'simple'); + this.debugUx.set((this.configurationManager.hasDebuggers() && !!this.configurationManager.selectedConfiguration.name) ? 'default' : 'simple'); this.breakpointsExist = CONTEXT_BREAKPOINTS_EXIST.bindTo(contextKeyService); }); @@ -161,7 +161,7 @@ export class DebugService implements IDebugService { this.onStateChange(); })); this.toDispose.push(this.configurationManager.onDidSelectConfiguration(() => { - this.debugUx.set(!!(this.state !== State.Inactive || this.configurationManager.selectedConfiguration.name) ? 'default' : 'simple'); + this.debugUx.set(!!(this.state !== State.Inactive || (this.configurationManager.selectedConfiguration.name && this.configurationManager.hasDebuggers())) ? 'default' : 'simple'); })); this.toDispose.push(this.model.onDidChangeCallStack(() => { const numberOfSessions = this.model.getSessions().filter(s => !s.parentSession).length; @@ -243,7 +243,7 @@ export class DebugService implements IDebugService { this.debugState.set(getStateLabel(state)); this.inDebugMode.set(state !== State.Inactive); // Only show the simple ux if debug is not yet started and if no launch.json exists - this.debugUx.set(((state !== State.Inactive && state !== State.Initializing) || this.configurationManager.selectedConfiguration.name) ? 'default' : 'simple'); + this.debugUx.set(((state !== State.Inactive && state !== State.Initializing) || (this.configurationManager.hasDebuggers() && this.configurationManager.selectedConfiguration.name)) ? 'default' : 'simple'); }); this.previousState = state; this._onDidChangeState.fire(state); From 21ebf4d282accd98b58bc118bff9b38d562309b4 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 28 Aug 2020 12:44:26 +0200 Subject: [PATCH 650/736] Revert "re #102503. allow fold/unfold with levels and direction in notebook." This reverts commit b4b67b8e7eacafb504e545a05f31a60012df20e7. --- .../notebook/browser/contrib/fold/folding.ts | 224 ++++++------------ .../browser/contrib/fold/foldingModel.ts | 71 +----- 2 files changed, 71 insertions(+), 224 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts b/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts index 9d888af88b8..814dc3a2527 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts @@ -9,15 +9,15 @@ import * as DOM from 'vs/base/browser/dom'; import { CellFoldingState, FoldingModel } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel'; import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; -import { MenuRegistry, MenuId, ICommandAction } from 'vs/platform/actions/common/actions'; +import { registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { getActiveNotebookEditor, NOTEBOOK_ACTIONS_CATEGORY } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; import { localize } from 'vs/nls'; -import { FoldingRegion } from 'vs/editor/contrib/folding/foldingRanges'; export class FoldingController extends Disposable implements INotebookEditorContribution { static id: string = 'workbench.notebook.findController'; @@ -65,31 +65,19 @@ export class FoldingController extends Disposable implements INotebookEditorCont this._updateEditorFoldingRanges(); } - setFoldingStateDown(index: number, state: CellFoldingState, levels: number) { - const doCollapse = state === CellFoldingState.Collapsed; - let region = this._foldingModel!.getRegionAtLine(index + 1); - let regions: FoldingRegion[] = []; - if (region) { - if (region.isCollapsed !== doCollapse) { - regions.push(region); - } - if (levels > 1) { - let regionsInside = this._foldingModel!.getRegionsInside(region, (r, level: number) => r.isCollapsed !== doCollapse && level < levels); - regions.push(...regionsInside); - } - } - - regions.forEach(r => this._foldingModel!.setCollapsed(r.regionIndex, state === CellFoldingState.Collapsed)); - this._updateEditorFoldingRanges(); - } - - setFoldingStateUp(index: number, state: CellFoldingState, levels: number) { + setFoldingState(index: number, state: CellFoldingState) { if (!this._foldingModel) { return; } - let regions = this._foldingModel.getAllRegionsAtLine(index + 1, (region, level) => region.isCollapsed !== (state === CellFoldingState.Collapsed) && level <= levels); - regions.forEach(r => this._foldingModel!.setCollapsed(r.regionIndex, state === CellFoldingState.Collapsed)); + const range = this._foldingModel.regions.findRange(index + 1); + const startIndex = this._foldingModel.regions.getStartLineNumber(range) - 1; + + if (startIndex !== index) { + return; + } + + this._foldingModel.setCollapsed(range, state === CellFoldingState.Collapsed); this._updateEditorFoldingRanges(); } @@ -133,7 +121,7 @@ export class FoldingController extends Disposable implements INotebookEditorCont return; } - this.setFoldingStateUp(modelIndex, state === CellFoldingState.Collapsed ? CellFoldingState.Expanded : CellFoldingState.Collapsed, 1); + this.setFoldingState(modelIndex, state === CellFoldingState.Collapsed ? CellFoldingState.Expanded : CellFoldingState.Collapsed); } return; @@ -142,47 +130,28 @@ export class FoldingController extends Disposable implements INotebookEditorCont registerNotebookContribution(FoldingController.id, FoldingController); -const NOTEBOOK_UNFOLD_COMMAND_ID = 'notebook.unfold'; -const NOTEBOOK_UNFOLD_COMMAND_LABEL = localize('unfold.cell', "Unfold Cell"); -const NOTEBOOK_FOLD_COMMAND_ID = 'notebook.fold'; -const NOTEBOOK_FOLD_COMMAND_LABEL = localize('fold.cell', "Fold Cell"); +registerAction2(class extends Action2 { + constructor() { + super({ + id: 'notebook.fold', + title: { value: localize('fold.cell', "Fold Cell"), original: 'Fold Cell' }, + category: NOTEBOOK_ACTIONS_CATEGORY, + keybinding: { + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_OPEN_SQUARE_BRACKET, + mac: { + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_OPEN_SQUARE_BRACKET, + secondary: [KeyCode.LeftArrow], + }, + secondary: [KeyCode.LeftArrow], + weight: KeybindingWeight.WorkbenchContrib + }, + precondition: NOTEBOOK_IS_ACTIVE_EDITOR, + f1: true + }); + } -KeybindingsRegistry.registerCommandAndKeybindingRule({ - weight: KeybindingWeight.WorkbenchContrib, - when: null, - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_CLOSE_SQUARE_BRACKET, - mac: { - primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_CLOSE_SQUARE_BRACKET, - secondary: [KeyCode.RightArrow], - }, - secondary: [KeyCode.RightArrow], - id: NOTEBOOK_UNFOLD_COMMAND_ID, - description: { - description: NOTEBOOK_UNFOLD_COMMAND_LABEL, - args: [ - { - name: 'index', description: 'The cell index', schema: { - 'type': 'object', - 'required': ['index', 'direction'], - 'properties': { - 'index': { - 'type': 'number' - }, - 'direction': { - 'type': 'string', - 'enum': ['up', 'down'], - 'default': 'down' - }, - 'levels': { - 'type': 'number', - 'default': 1 - }, - } - } - } - ] - }, - handler: async (accessor, args?: { index: number, levels: number, direction: 'up' | 'down' }) => { + async run(accessor: ServicesAccessor): Promise { const editorService = accessor.get(IEditorService); const editor = getActiveNotebookEditor(editorService); @@ -190,78 +159,43 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ return; } - const levels = args && args.levels || 1; - const direction = args && args.direction === 'up' ? 'up' : 'down'; - let index: number | undefined = undefined; - - if (args) { - index = args.index; - } else { - const activeCell = editor.getActiveCell(); - if (!activeCell) { - return; - } - index = editor.viewModel?.viewCells.indexOf(activeCell); + const activeCell = editor.getActiveCell(); + if (!activeCell) { + return; } const controller = editor.getContribution(FoldingController.id); + + const index = editor.viewModel?.viewCells.indexOf(activeCell); + if (index !== undefined) { - if (direction === 'up') { - controller.setFoldingStateUp(index, CellFoldingState.Expanded, levels); - } else { - controller.setFoldingStateDown(index, CellFoldingState.Expanded, levels); - } + controller.setFoldingState(index, CellFoldingState.Collapsed); } } }); -const unfoldCommand: ICommandAction = { - id: NOTEBOOK_UNFOLD_COMMAND_ID, - title: { value: NOTEBOOK_UNFOLD_COMMAND_LABEL, original: 'Unfold Cell' }, - category: NOTEBOOK_ACTIONS_CATEGORY, - precondition: NOTEBOOK_IS_ACTIVE_EDITOR -}; +registerAction2(class extends Action2 { + constructor() { + super({ + id: 'notebook.unfold', + title: { value: localize('unfold.cell', "Unfold Cell"), original: 'Unfold Cell' }, + category: NOTEBOOK_ACTIONS_CATEGORY, + keybinding: { + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_CLOSE_SQUARE_BRACKET, + mac: { + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_CLOSE_SQUARE_BRACKET, + secondary: [KeyCode.RightArrow], + }, + secondary: [KeyCode.RightArrow], + weight: KeybindingWeight.WorkbenchContrib + }, + precondition: NOTEBOOK_IS_ACTIVE_EDITOR, + f1: true + }); + } -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: unfoldCommand, when: unfoldCommand.precondition }); -MenuRegistry.addCommand(unfoldCommand); - - -KeybindingsRegistry.registerCommandAndKeybindingRule({ - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_OPEN_SQUARE_BRACKET, - mac: { - primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_OPEN_SQUARE_BRACKET, - secondary: [KeyCode.LeftArrow], - }, - secondary: [KeyCode.LeftArrow], - weight: KeybindingWeight.WorkbenchContrib, - id: NOTEBOOK_FOLD_COMMAND_ID, - description: { - description: NOTEBOOK_FOLD_COMMAND_LABEL, - args: [ - { - name: 'index', description: 'The cell index', schema: { - 'type': 'object', - 'required': ['index', 'direction'], - 'properties': { - 'index': { - 'type': 'number' - }, - 'direction': { - 'type': 'string', - 'enum': ['up', 'down'], - 'default': 'down' - }, - 'levels': { - 'type': 'number', - 'default': 1 - }, - } - } - } - ] - }, - handler: async (accessor, args?: { index: number, levels: number, direction: 'up' | 'down' }) => { + async run(accessor: ServicesAccessor): Promise { const editorService = accessor.get(IEditorService); const editor = getActiveNotebookEditor(editorService); @@ -269,37 +203,17 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ return; } - const levels = args && args.levels || 1; - const direction = args && args.direction === 'up' ? 'up' : 'down'; - let index: number | undefined = undefined; - - if (args) { - index = args.index; - } else { - const activeCell = editor.getActiveCell(); - if (!activeCell) { - return; - } - index = editor.viewModel?.viewCells.indexOf(activeCell); + const activeCell = editor.getActiveCell(); + if (!activeCell) { + return; } const controller = editor.getContribution(FoldingController.id); + + const index = editor.viewModel?.viewCells.indexOf(activeCell); + if (index !== undefined) { - if (direction === 'up') { - controller.setFoldingStateUp(index, CellFoldingState.Collapsed, levels); - } else { - controller.setFoldingStateDown(index, CellFoldingState.Collapsed, levels); - } + controller.setFoldingState(index, CellFoldingState.Expanded); } } }); - -const foldCommand: ICommandAction = { - id: NOTEBOOK_FOLD_COMMAND_ID, - title: { value: NOTEBOOK_FOLD_COMMAND_LABEL, original: 'Fold Cell' }, - category: NOTEBOOK_ACTIONS_CATEGORY, - precondition: NOTEBOOK_IS_ACTIVE_EDITOR -}; - -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: foldCommand, when: foldCommand.precondition }); -MenuRegistry.addCommand(foldCommand); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts b/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts index fade3589aa0..18f853b05e5 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts @@ -6,16 +6,12 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { TrackedRangeStickiness } from 'vs/editor/common/model'; -import { FoldingRegion, FoldingRegions } from 'vs/editor/contrib/folding/foldingRanges'; +import { FoldingRegions } from 'vs/editor/contrib/folding/foldingRanges'; import { IFoldingRangeData, sanitizeRanges } from 'vs/editor/contrib/folding/syntaxRangeProvider'; import { ICellRange } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -type RegionFilter = (r: FoldingRegion) => boolean; -type RegionFilterWithLevel = (r: FoldingRegion, level: number) => boolean; - - export class FoldingModel extends Disposable { private _viewModel: NotebookViewModel | null = null; private _viewModelStore = new DisposableStore(); @@ -77,70 +73,7 @@ export class FoldingModel extends Disposable { this.recompute(); } - getRegionAtLine(lineNumber: number): FoldingRegion | null { - if (this._regions) { - let index = this._regions.findRange(lineNumber); - if (index >= 0) { - return this._regions.toRegion(index); - } - } - return null; - } - - getRegionsInside(region: FoldingRegion | null, filter?: RegionFilter | RegionFilterWithLevel): FoldingRegion[] { - let result: FoldingRegion[] = []; - let index = region ? region.regionIndex + 1 : 0; - let endLineNumber = region ? region.endLineNumber : Number.MAX_VALUE; - - if (filter && filter.length === 2) { - const levelStack: FoldingRegion[] = []; - for (let i = index, len = this._regions.length; i < len; i++) { - let current = this._regions.toRegion(i); - if (this._regions.getStartLineNumber(i) < endLineNumber) { - while (levelStack.length > 0 && !current.containedBy(levelStack[levelStack.length - 1])) { - levelStack.pop(); - } - levelStack.push(current); - if (filter(current, levelStack.length)) { - result.push(current); - } - } else { - break; - } - } - } else { - for (let i = index, len = this._regions.length; i < len; i++) { - let current = this._regions.toRegion(i); - if (this._regions.getStartLineNumber(i) < endLineNumber) { - if (!filter || (filter as RegionFilter)(current)) { - result.push(current); - } - } else { - break; - } - } - } - return result; - } - - getAllRegionsAtLine(lineNumber: number, filter?: (r: FoldingRegion, level: number) => boolean): FoldingRegion[] { - let result: FoldingRegion[] = []; - if (this._regions) { - let index = this._regions.findRange(lineNumber); - let level = 1; - while (index >= 0) { - let current = this._regions.toRegion(index); - if (!filter || filter(current, level)) { - result.push(current); - } - level++; - index = current.parentIndex; - } - } - return result; - } - - setCollapsed(index: number, newState: boolean) { + public setCollapsed(index: number, newState: boolean) { this._regions.setCollapsed(index, newState); } From 59b5a2f060ac0d559e7b81097e92139ebdd5e810 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 28 Aug 2020 12:55:13 +0200 Subject: [PATCH 651/736] include extensions in the text --- .../contrib/extensions/browser/exeBasedRecommendations.ts | 2 +- .../extensions/browser/extensionRecommendationsService.ts | 2 +- .../contrib/extensions/browser/fileBasedRecommendations.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts index 52b4b91a0c6..5d437ed7761 100644 --- a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts @@ -141,7 +141,7 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { await this.tasExperimentService.getTreatment('wslpopupaa'); } - const message = localize('exeRecommended', "You have {0} installed on your system. Do you want to install recommendations for it?", tips[0].exeFriendlyName); + const message = localize('exeRecommended', "You have {0} installed on your system. Do you want to install recommended extensions for it?", tips[0].exeFriendlyName); this.promptImportantExtensionsInstallNotification(extensionIds, message, `@exe:"${tips[0].exeName}"`); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts index 7a04a1f71c2..1c59e80ef47 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts @@ -330,7 +330,7 @@ export class ExtensionRecommendationsService extends Disposable implements IExte const searchValue = '@recommended '; this.notificationService.prompt( Severity.Info, - localize('workspaceRecommended', "Do you want to install recommendations for this repository?"), + localize('workspaceRecommended', "Do you want to install recommended extensions for this repository?"), [{ label: localize('install', "Install"), run: async () => { diff --git a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts index 6c3ed3be9d3..b2f564cc9f6 100644 --- a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts @@ -232,7 +232,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { return false; } - this.promptImportantExtensionsInstallNotification([extensionId], localize('reallyRecommended', "Do you want to install recommendations for {0}?", languageName), `@id:${extensionId}`); + this.promptImportantExtensionsInstallNotification([extensionId], localize('reallyRecommended', "Do you want to install recommended extensions for {0}?", languageName), `@id:${extensionId}`); return true; } From ab2f68ff1a2e8ba26020fd151e076c81fee1f93b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 28 Aug 2020 12:58:57 +0200 Subject: [PATCH 652/736] fixing grammar --- .../contrib/extensions/browser/exeBasedRecommendations.ts | 2 +- .../extensions/browser/extensionRecommendationsService.ts | 2 +- .../contrib/extensions/browser/fileBasedRecommendations.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts index 5d437ed7761..a9911fa979a 100644 --- a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts @@ -141,7 +141,7 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { await this.tasExperimentService.getTreatment('wslpopupaa'); } - const message = localize('exeRecommended', "You have {0} installed on your system. Do you want to install recommended extensions for it?", tips[0].exeFriendlyName); + const message = localize('exeRecommended', "You have {0} installed on your system. Do you want to install the recommended extensions for it?", tips[0].exeFriendlyName); this.promptImportantExtensionsInstallNotification(extensionIds, message, `@exe:"${tips[0].exeName}"`); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts index 1c59e80ef47..1282981f9bd 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts @@ -330,7 +330,7 @@ export class ExtensionRecommendationsService extends Disposable implements IExte const searchValue = '@recommended '; this.notificationService.prompt( Severity.Info, - localize('workspaceRecommended', "Do you want to install recommended extensions for this repository?"), + localize('workspaceRecommended', "Do you want to install the recommended extensions for this repository?"), [{ label: localize('install', "Install"), run: async () => { diff --git a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts index b2f564cc9f6..0b1024e6eac 100644 --- a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts @@ -232,7 +232,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { return false; } - this.promptImportantExtensionsInstallNotification([extensionId], localize('reallyRecommended', "Do you want to install recommended extensions for {0}?", languageName), `@id:${extensionId}`); + this.promptImportantExtensionsInstallNotification([extensionId], localize('reallyRecommended', "Do you want to install the recommended extensions for {0}?", languageName), `@id:${extensionId}`); return true; } From 4fbb52bb30886da685d1f4e10dfdc623088ae3ea Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 28 Aug 2020 13:10:04 +0200 Subject: [PATCH 653/736] add trace logging to know what keybinding triggers what command, fyi @alexdima --- src/vs/editor/standalone/browser/simpleServices.ts | 3 ++- .../platform/keybinding/common/abstractKeybindingService.ts | 4 ++++ .../keybinding/test/common/abstractKeybindingService.test.ts | 3 ++- .../services/keybinding/browser/keybindingService.ts | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 01554deb137..b336adc20cb 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -46,6 +46,7 @@ import { SimpleServicesNLS } from 'vs/editor/common/standaloneStrings'; import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings'; import { basename } from 'vs/base/common/resources'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { NullLogService } from 'vs/platform/log/common/log'; export class SimpleModel implements IResolvedTextEditorModel { @@ -292,7 +293,7 @@ export class StandaloneKeybindingService extends AbstractKeybindingService { notificationService: INotificationService, domNode: HTMLElement ) { - super(contextKeyService, commandService, telemetryService, notificationService); + super(contextKeyService, commandService, telemetryService, notificationService, new NullLogService()); this._cachedResolver = null; this._dynamicKeybindings = []; diff --git a/src/vs/platform/keybinding/common/abstractKeybindingService.ts b/src/vs/platform/keybinding/common/abstractKeybindingService.ts index 1fce530772a..c6e754997f1 100644 --- a/src/vs/platform/keybinding/common/abstractKeybindingService.ts +++ b/src/vs/platform/keybinding/common/abstractKeybindingService.ts @@ -17,6 +17,7 @@ import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKe import { INotificationService } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; +import { ILogService } from 'vs/platform/log/common/log'; interface CurrentChord { keypress: string; @@ -44,6 +45,7 @@ export abstract class AbstractKeybindingService extends Disposable implements IK protected _commandService: ICommandService, protected _telemetryService: ITelemetryService, private _notificationService: INotificationService, + protected _logService: ILogService, ) { super(); @@ -177,6 +179,8 @@ export abstract class AbstractKeybindingService extends Disposable implements IK const keypressLabel = keybinding.getLabel(); const resolveResult = this._getResolver().resolve(contextValue, currentChord, firstPart); + this._logService.trace('KeybindingService#dispatch', keypressLabel, resolveResult?.commandId); + if (resolveResult && resolveResult.enterChord) { shouldPreventDefault = true; this._enterChordMode(firstPart, keypressLabel); diff --git a/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts b/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts index 286ea8a57ae..5149a9202cc 100644 --- a/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts +++ b/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts @@ -16,6 +16,7 @@ import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayo import { INotification, INotificationService, IPromptChoice, IPromptOptions, NoOpNotification, IStatusMessageOptions } from 'vs/platform/notification/common/notification'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { Disposable } from 'vs/base/common/lifecycle'; +import { NullLogService } from 'vs/platform/log/common/log'; function createContext(ctx: any) { return { @@ -36,7 +37,7 @@ suite('AbstractKeybindingService', () => { commandService: ICommandService, notificationService: INotificationService ) { - super(contextKeyService, commandService, NullTelemetryService, notificationService); + super(contextKeyService, commandService, NullTelemetryService, notificationService, new NullLogService()); this._resolver = resolver; } diff --git a/src/vs/workbench/services/keybinding/browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts index a9dc027018d..34ea342cc80 100644 --- a/src/vs/workbench/services/keybinding/browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -194,7 +194,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { @ILogService logService: ILogService, @IKeymapService private readonly keymapService: IKeymapService ) { - super(contextKeyService, commandService, telemetryService, notificationService); + super(contextKeyService, commandService, telemetryService, notificationService, logService); this.updateSchema(); From bb9287bb4f11fbd5a94a84f08541411f640faa2d Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 28 Aug 2020 13:49:08 +0200 Subject: [PATCH 654/736] Revert "Use DOM API (fixes #105505)" This reverts commit e995f1095805e726c505ebb6477f2b6030a28cc7. --- .../welcome/overlay/browser/welcomeOverlay.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts b/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts index c1c19309f36..33bdbf1ba9d 100644 --- a/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts +++ b/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts @@ -38,43 +38,43 @@ interface Key { const keys: Key[] = [ { id: 'explorer', - arrow: '←', + arrow: '←', label: localize('welcomeOverlay.explorer', "File explorer"), command: 'workbench.view.explorer' }, { id: 'search', - arrow: '←', + arrow: '←', label: localize('welcomeOverlay.search', "Search across files"), command: 'workbench.view.search' }, { id: 'git', - arrow: '←', + arrow: '←', label: localize('welcomeOverlay.git', "Source code management"), command: 'workbench.view.scm' }, { id: 'debug', - arrow: '←', + arrow: '←', label: localize('welcomeOverlay.debug', "Launch and debug"), command: 'workbench.view.debug' }, { id: 'extensions', - arrow: '←', + arrow: '←', label: localize('welcomeOverlay.extensions', "Manage extensions"), command: 'workbench.view.extensions' }, // { // id: 'watermark', - // arrow: '⤹', + // arrow: '⤹', // label: localize('welcomeOverlay.watermark', "Command Hints"), // withEditor: false // }, { id: 'problems', - arrow: '⤹', + arrow: '⤹', label: localize('welcomeOverlay.problems', "View errors and warnings"), command: 'workbench.actions.view.problems' }, @@ -85,20 +85,20 @@ const keys: Key[] = [ }, // { // id: 'openfile', - // arrow: '⤸', + // arrow: '⤸', // label: localize('welcomeOverlay.openfile', "File Properties"), // arrowLast: true, // withEditor: true // }, { id: 'commandPalette', - arrow: '↖', + arrow: '↖', label: localize('welcomeOverlay.commandPalette', "Find and run all commands"), command: ShowAllCommandsAction.ID }, { id: 'notifications', - arrow: '⤵', + arrow: '⤵', arrowLast: true, label: localize('welcomeOverlay.notifications', "Show notifications"), command: 'notifications.showList' @@ -186,7 +186,7 @@ class WelcomeOverlay extends Disposable { .forEach(({ id, arrow, label, command, arrowLast }) => { const div = dom.append(this._overlay, $(`.key.${id}`)); if (arrow && !arrowLast) { - dom.append(div, $('span.arrow', {}, arrow)); + dom.append(div, $('span.arrow')).innerHTML = arrow; } dom.append(div, $('span.label')).textContent = label; if (command) { @@ -196,7 +196,7 @@ class WelcomeOverlay extends Disposable { } } if (arrow && arrowLast) { - dom.append(div, $('span.arrow', {}, arrow)); + dom.append(div, $('span.arrow')).innerHTML = arrow; } }); } From 44700f81bf21d9bacf281eca703e6b5c45be14ee Mon Sep 17 00:00:00 2001 From: annkamsk Date: Thu, 27 Aug 2020 17:23:19 +0200 Subject: [PATCH 655/736] Set HTMLElement's content by providing it as a child, not by .innerHTML --- .../welcome/overlay/browser/welcomeOverlay.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts b/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts index 33bdbf1ba9d..0c5abc86c18 100644 --- a/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts +++ b/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts @@ -38,31 +38,31 @@ interface Key { const keys: Key[] = [ { id: 'explorer', - arrow: '←', + arrow: '\u2190', // ← label: localize('welcomeOverlay.explorer', "File explorer"), command: 'workbench.view.explorer' }, { id: 'search', - arrow: '←', + arrow: '\u2190', // ← label: localize('welcomeOverlay.search', "Search across files"), command: 'workbench.view.search' }, { id: 'git', - arrow: '←', + arrow: '\u2190', // ← label: localize('welcomeOverlay.git', "Source code management"), command: 'workbench.view.scm' }, { id: 'debug', - arrow: '←', + arrow: '\u2190', // ← label: localize('welcomeOverlay.debug', "Launch and debug"), command: 'workbench.view.debug' }, { id: 'extensions', - arrow: '←', + arrow: '\u2190', // ← label: localize('welcomeOverlay.extensions', "Manage extensions"), command: 'workbench.view.extensions' }, @@ -74,7 +74,7 @@ const keys: Key[] = [ // }, { id: 'problems', - arrow: '⤹', + arrow: '\u2939', // ⤹ label: localize('welcomeOverlay.problems', "View errors and warnings"), command: 'workbench.actions.view.problems' }, @@ -92,13 +92,13 @@ const keys: Key[] = [ // }, { id: 'commandPalette', - arrow: '↖', + arrow: '\u2196', // ↖ label: localize('welcomeOverlay.commandPalette', "Find and run all commands"), command: ShowAllCommandsAction.ID }, { id: 'notifications', - arrow: '⤵', + arrow: '\u2935', // ⤵ arrowLast: true, label: localize('welcomeOverlay.notifications', "Show notifications"), command: 'notifications.showList' @@ -186,7 +186,7 @@ class WelcomeOverlay extends Disposable { .forEach(({ id, arrow, label, command, arrowLast }) => { const div = dom.append(this._overlay, $(`.key.${id}`)); if (arrow && !arrowLast) { - dom.append(div, $('span.arrow')).innerHTML = arrow; + dom.append(div, $('span.arrow', undefined, arrow)); } dom.append(div, $('span.label')).textContent = label; if (command) { @@ -196,7 +196,7 @@ class WelcomeOverlay extends Disposable { } } if (arrow && arrowLast) { - dom.append(div, $('span.arrow')).innerHTML = arrow; + dom.append(div, $('span.arrow', undefined, arrow)); } }); } From 1ad6332a3dac761fb0824c9e398f381182c0f6bf Mon Sep 17 00:00:00 2001 From: annkamsk Date: Fri, 28 Aug 2020 14:33:25 +0200 Subject: [PATCH 656/736] Create HTML with dom.ts#$ function calls instead of string concatenation --- .../inspectEditorTokens.ts | 182 +++++++++++------- 1 file changed, 111 insertions(+), 71 deletions(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts index d4ca7479c7f..16f632117f0 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts @@ -10,7 +10,6 @@ import { CharCode } from 'vs/base/common/charCode'; import { Color } from 'vs/base/common/color'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; -import { escape } from 'vs/base/common/strings'; import { ContentWidgetPositionPreference, IActiveCodeEditor, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; @@ -31,6 +30,8 @@ import { SemanticTokenRule, TokenStyleData, TokenStyle } from 'vs/platform/theme import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { SEMANTIC_HIGHLIGHTING_SETTING_ID, IEditorSemanticHighlightingOptions } from 'vs/editor/common/services/modelServiceImpl'; +const $ = dom.$; + class InspectEditorTokensController extends Disposable implements IEditorContribution { public static readonly ID = 'editor.contrib.inspectEditorTokens'; @@ -151,23 +152,11 @@ function renderTokenText(tokenText: string): string { let charCode = tokenText.charCodeAt(charIndex); switch (charCode) { case CharCode.Tab: - result += '→'; + result += '\u2192'; // → break; case CharCode.Space: - result += '·'; - break; - - case CharCode.LessThan: - result += '<'; - break; - - case CharCode.GreaterThan: - result += '>'; - break; - - case CharCode.Ampersand: - result += '&'; + result += '\u00B7'; // · break; default: @@ -246,8 +235,7 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget { if (this._isDisposed) { return; } - let text = this._compute(grammar, semanticTokens, position); - this._domNode.innerHTML = text; + this._compute(grammar, semanticTokens, position); this._domNode.style.maxWidth = `${Math.max(this._editor.getLayoutInfo().width * 0.66, 500)}px`; this._editor.layoutContentWidget(this); }, (err) => { @@ -268,11 +256,12 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget { return this._themeService.getColorTheme().semanticHighlighting; } - private _compute(grammar: IGrammar | null, semanticTokens: SemanticTokensResult | null, position: Position): string { + private _compute(grammar: IGrammar | null, semanticTokens: SemanticTokensResult | null, position: Position) { const textMateTokenInfo = grammar && this._getTokensAtPosition(grammar, position); const semanticTokenInfo = semanticTokens && this._getSemanticTokenAtPosition(semanticTokens, position); if (!textMateTokenInfo && !semanticTokenInfo) { - return 'No grammar or semantic tokens available.'; + dom.reset(this._domNode, 'No grammar or semantic tokens available.'); + return; } let tmMetadata = textMateTokenInfo?.metadata; @@ -283,91 +272,125 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget { const tokenText = semTokenText || tmTokenText || ''; - let result = ''; - result += `

    ${tokenText}(${tokenText.length} ${tokenText.length === 1 ? 'char' : 'chars'})

    `; - result += ``; - - result += ``; - result += ``; - result += ``; - - result += this._formatMetadata(semMetadata, tmMetadata); - result += ``; + dom.reset(this._domNode, + $('h2.tiw-token', undefined, + tokenText, + $('span.tiw-token-length', undefined, `${tokenText.length} ${tokenText.length === 1 ? 'char' : 'chars'}`))); + dom.append(this._domNode, $('hr.tiw-metadata-separator', { 'style': 'clear:both' })); + dom.append(this._domNode, $('table.tiw-metadata-table', undefined, + $('tbody', undefined, + $('tr', undefined, + $('td.tiw-metadata-key', undefined, 'language'), + $('td.tiw-metadata-value', undefined, tmMetadata?.languageIdentifier.language || '') + ), + $('tr', undefined, + $('td.tiw-metadata-key', undefined, 'standard token type' as string), + $('td.tiw-metadata-value', undefined, this._tokenTypeToString(tmMetadata?.tokenType || StandardTokenType.Other)) + ), + ...this._formatMetadata(semMetadata, tmMetadata) + ) + )); if (semanticTokenInfo) { - result += ``; - result += ``; - result += ``; + dom.append(this._domNode, $('hr.tiw-metadata-separator')); + const table = dom.append(this._domNode, $('table.tiw-metadata-table', undefined)); + const tbody = dom.append(table, $('tbody', undefined, + $('tr', undefined, + $('td.tiw-metadata-key', undefined, 'semantic token type' as string), + $('td.tiw-metadata-value', undefined, semanticTokenInfo.type) + ) + )); if (semanticTokenInfo.modifiers.length) { - result += ``; + dom.append(tbody, $('tr', undefined, + $('td.tiw-metadata-key', undefined, 'modifiers'), + $('td.tiw-metadata-value', undefined, semanticTokenInfo.modifiers.join(' ')), + )); } if (semanticTokenInfo.metadata) { const properties: (keyof TokenStyleData)[] = ['foreground', 'bold', 'italic', 'underline']; const propertiesByDefValue: { [rule: string]: string[] } = {}; - const allDefValues = []; // remember the order + const allDefValues = new Array<[Array, string]>(); // remember the order // first collect to detect when the same rule is used for multiple properties for (let property of properties) { if (semanticTokenInfo.metadata[property] !== undefined) { const definition = semanticTokenInfo.definitions[property]; const defValue = this._renderTokenStyleDefinition(definition, property); - let properties = propertiesByDefValue[defValue]; + const defValueStr = defValue.map(el => el instanceof HTMLElement ? el.outerHTML : el).join(); + let properties = propertiesByDefValue[defValueStr]; if (!properties) { - propertiesByDefValue[defValue] = properties = []; - allDefValues.push(defValue); + propertiesByDefValue[defValueStr] = properties = []; + allDefValues.push([defValue, defValueStr]); } properties.push(property); } } - for (let defValue of allDefValues) { - result += ``; + for (const [defValue, defValueStr] of allDefValues) { + dom.append(tbody, $('tr', undefined, + $('td.tiw-metadata-key', undefined, propertiesByDefValue[defValueStr].join(', ')), + $('td.tiw-metadata-value', undefined, ...defValue) + )); } } - result += ``; } if (textMateTokenInfo) { let theme = this._themeService.getColorTheme(); - result += ``; - result += ``; + dom.append(this._domNode, $('hr.tiw-metadata-separator')); + const table = dom.append(this._domNode, $('table.tiw-metadata-table')); + const tbody = dom.append(table, $('tbody')); + if (tmTokenText && tmTokenText !== tokenText) { - result += ``; + dom.append(tbody, $('tr', undefined, + $('td.tiw-metadata-key', undefined, 'textmate token' as string), + $('td.tiw-metadata-value', undefined, `${tmTokenText} (${tmTokenText.length})`) + )); } - let scopes = ''; + const scopes = new Array(); for (let i = textMateTokenInfo.token.scopes.length - 1; i >= 0; i--) { - scopes += escape(textMateTokenInfo.token.scopes[i]); + scopes.push(textMateTokenInfo.token.scopes[i]); if (i > 0) { - scopes += '
    '; + scopes.push($('br')); } } - result += `
    `; + dom.append(tbody, $('tr', undefined, + $('td.tiw-metadata-key', undefined, 'textmate scopes' as string), + $('td.tiw-metadata-value.tiw-metadata-scopes', undefined, ...scopes), + )); let matchingRule = findMatchingThemeRule(theme, textMateTokenInfo.token.scopes, false); const semForeground = semanticTokenInfo?.metadata?.foreground; if (matchingRule) { - let defValue = `${matchingRule.rawSelector}\n${JSON.stringify(matchingRule.settings, null, '\t')}`; if (semForeground !== textMateTokenInfo.metadata.foreground) { + let defValue = $('code.tiw-theme-selector', undefined, + matchingRule.rawSelector, $('br'), JSON.stringify(matchingRule.settings, null, '\t')); if (semForeground) { - defValue = `${defValue}`; + defValue = $('s', undefined, defValue); } - result += ``; + dom.append(tbody, $('tr', undefined, + $('td.tiw-metadata-key', undefined, 'foreground'), + $('td.tiw-metadata-value', undefined, defValue), + )); } } else if (!semForeground) { - result += ``; + dom.append(tbody, $('tr', undefined, + $('td.tiw-metadata-key', undefined, 'foreground'), + $('td.tiw-metadata-value', undefined, 'No theme selector' as string), + )); } - result += ``; } - return result; } - private _formatMetadata(semantic?: IDecodedMetadata, tm?: IDecodedMetadata) { - let result = ''; + private _formatMetadata(semantic?: IDecodedMetadata, tm?: IDecodedMetadata): Array { + const elements = new Array(); function render(property: 'foreground' | 'background') { let value = semantic?.[property] || tm?.[property]; if (value !== undefined) { const semanticStyle = semantic?.[property] ? 'tiw-metadata-semantic' : ''; - result += `${property}${value}`; - + elements.push($('tr', undefined, + $('td.tiw-metadata-key', undefined, property), + $(`td.tiw-metadata-value.${semanticStyle}`, undefined, value) + )); } return value; } @@ -377,17 +400,23 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget { if (foreground && background) { const backgroundColor = Color.fromHex(background), foregroundColor = Color.fromHex(foreground); if (backgroundColor.isOpaque()) { - result += `contrast ratio${backgroundColor.getContrastRatio(foregroundColor.makeOpaque(backgroundColor)).toFixed(2)}`; + elements.push($('tr', undefined, + $('td.tiw-metadata-key', undefined, 'contrast ratio' as string), + $('td.tiw-metadata-value', undefined, backgroundColor.getContrastRatio(foregroundColor.makeOpaque(backgroundColor)).toFixed(2)) + )); } else { - result += 'Contrast ratio cannot be precise for background colors that use transparency'; + elements.push($('tr', undefined, + $('td.tiw-metadata-key', undefined, 'Contrast ratio cannot be precise for background colors that use transparency' as string), + $('td.tiw-metadata-value') + )); } } - let fontStyleLabels: string[] = []; + const fontStyleLabels = new Array(); function addStyle(key: 'bold' | 'italic' | 'underline') { if (semantic && semantic[key]) { - fontStyleLabels.push(``); + fontStyleLabels.push($('span.tiw-metadata-semantic', undefined, key)); } else if (tm && tm[key]) { fontStyleLabels.push(key); } @@ -396,9 +425,12 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget { addStyle('italic'); addStyle('underline'); if (fontStyleLabels.length) { - result += `font style${fontStyleLabels.join(' ')}`; + elements.push($('tr', undefined, + $('td.tiw-metadata-key', undefined, 'font style' as string), + $('td.tiw-metadata-value', undefined, fontStyleLabels.join(' ')) + )); } - return result; + return elements; } private _decodeMetadata(metadata: number): IDecodedMetadata { @@ -549,9 +581,10 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget { return null; } - private _renderTokenStyleDefinition(definition: TokenStyleDefinition | undefined, property: keyof TokenStyleData): string { + private _renderTokenStyleDefinition(definition: TokenStyleDefinition | undefined, property: keyof TokenStyleData): Array { + const elements = new Array(); if (definition === undefined) { - return ''; + return elements; } const theme = this._themeService.getColorTheme() as ColorThemeData; @@ -561,20 +594,27 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget { const matchingRule = scopesDefinition[property]; if (matchingRule && scopesDefinition.scope) { const strScopes = Array.isArray(matchingRule.scope) ? matchingRule.scope.join(', ') : String(matchingRule.scope); - return `${escape(scopesDefinition.scope.join(' '))}
    ${strScopes}\n${JSON.stringify(matchingRule.settings, null, '\t')}`; + elements.push( + scopesDefinition.scope.join(' '), + $('br'), + $('code.tiw-theme-selector', undefined, strScopes, $('br'), JSON.stringify(matchingRule.settings, null, '\t'))); + return elements; } - return ''; + return elements; } else if (SemanticTokenRule.is(definition)) { const scope = theme.getTokenStylingRuleScope(definition); if (scope === 'setting') { - return `User settings: ${definition.selector.id} - ${this._renderStyleProperty(definition.style, property)}`; + elements.push(`User settings: ${definition.selector.id} - ${this._renderStyleProperty(definition.style, property)}`); + return elements; } else if (scope === 'theme') { - return `Color theme: ${definition.selector.id} - ${this._renderStyleProperty(definition.style, property)}`; + elements.push(`Color theme: ${definition.selector.id} - ${this._renderStyleProperty(definition.style, property)}`); + return elements; } - return ''; + return elements; } else { const style = theme.resolveTokenStyleValue(definition); - return `Default: ${style ? this._renderStyleProperty(style, property) : ''}`; + elements.push(`Default: ${style ? this._renderStyleProperty(style, property) : ''}`); + return elements; } } From 2a9a512ee4e4cd57f21551533c662cc612b831c4 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 28 Aug 2020 14:54:16 +0200 Subject: [PATCH 657/736] debug: react on registration to update context keys --- .../contrib/debug/browser/debugConfigurationManager.ts | 2 +- src/vs/workbench/contrib/debug/browser/debugService.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index b1d483a7755..e28eaf83505 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -99,6 +99,7 @@ export class ConfigurationManager implements IConfigurationManager { registerDebugAdapterFactory(debugTypes: string[], debugAdapterLauncher: IDebugAdapterFactory): IDisposable { debugTypes.forEach(debugType => this.debugAdapterFactories.set(debugType, debugAdapterLauncher)); this.debuggersAvailable.set(this.debugAdapterFactories.size > 0); + this._onDidRegisterDebugger.fire(); return { dispose: () => { @@ -425,7 +426,6 @@ export class ConfigurationManager implements IConfigurationManager { }); this.setCompoundSchemaValues(); - this._onDidRegisterDebugger.fire(); }); breakpointsExtPoint.setHandler((extensions, delta) => { diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 7f5e9a6e2a0..76d3fd1d18f 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -160,7 +160,7 @@ export class DebugService implements IDebugService { this.toDispose.push(this.viewModel.onDidFocusSession(() => { this.onStateChange(); })); - this.toDispose.push(this.configurationManager.onDidSelectConfiguration(() => { + this.toDispose.push(Event.any(this.configurationManager.onDidRegisterDebugger, this.configurationManager.onDidSelectConfiguration)(() => { this.debugUx.set(!!(this.state !== State.Inactive || (this.configurationManager.selectedConfiguration.name && this.configurationManager.hasDebuggers())) ? 'default' : 'simple'); })); this.toDispose.push(this.model.onDidChangeCallStack(() => { From bc3b0defe0de3239fa1021cef7db7def9deb3b68 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 28 Aug 2020 14:57:20 +0200 Subject: [PATCH 658/736] use extensionUri and joinPath instead of asAbsolutePath --- .../client/src/browser/cssClientMain.ts | 6 +++--- .../client/src/browser/htmlClientMain.ts | 6 +++--- .../client/src/browser/jsonClientMain.ts | 6 +++--- .../typescript-language-features/src/extension.browser.ts | 3 +-- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/extensions/css-language-features/client/src/browser/cssClientMain.ts b/extensions/css-language-features/client/src/browser/cssClientMain.ts index 8b1d7205fcd..2a5e3e1f2c2 100644 --- a/extensions/css-language-features/client/src/browser/cssClientMain.ts +++ b/extensions/css-language-features/client/src/browser/cssClientMain.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ExtensionContext } from 'vscode'; +import { ExtensionContext, Uri } from 'vscode'; import { LanguageClientOptions } from 'vscode-languageclient'; import { startClient, LanguageClientConstructor } from '../cssClient'; import { LanguageClient } from 'vscode-languageclient/browser'; @@ -17,9 +17,9 @@ declare const TextDecoder: { // this method is called when vs code is activated export function activate(context: ExtensionContext) { - const serverMain = context.asAbsolutePath('server/dist/browser/cssServerMain.js'); + const serverMain = Uri.joinPath(context.extensionUri, 'server/dist/browser/cssServerMain.js'); try { - const worker = new Worker(serverMain); + const worker = new Worker(serverMain.toString()); const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => { return new LanguageClient(id, name, clientOptions, worker); }; diff --git a/extensions/html-language-features/client/src/browser/htmlClientMain.ts b/extensions/html-language-features/client/src/browser/htmlClientMain.ts index 1623bea9e20..2ca84927f2d 100644 --- a/extensions/html-language-features/client/src/browser/htmlClientMain.ts +++ b/extensions/html-language-features/client/src/browser/htmlClientMain.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ExtensionContext } from 'vscode'; +import { ExtensionContext, Uri } from 'vscode'; import { LanguageClientOptions } from 'vscode-languageclient'; import { startClient, LanguageClientConstructor } from '../htmlClient'; import { LanguageClient } from 'vscode-languageclient/browser'; @@ -17,9 +17,9 @@ declare const TextDecoder: { // this method is called when vs code is activated export function activate(context: ExtensionContext) { - const serverMain = context.asAbsolutePath('server/dist/browser/htmlServerMain.js'); + const serverMain = Uri.joinPath(context.extensionUri, 'server/dist/browser/cssServerMain.js'); try { - const worker = new Worker(serverMain); + const worker = new Worker(serverMain.toString()); const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => { return new LanguageClient(id, name, clientOptions, worker); }; diff --git a/extensions/json-language-features/client/src/browser/jsonClientMain.ts b/extensions/json-language-features/client/src/browser/jsonClientMain.ts index 488343f42ca..18115fa175a 100644 --- a/extensions/json-language-features/client/src/browser/jsonClientMain.ts +++ b/extensions/json-language-features/client/src/browser/jsonClientMain.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ExtensionContext } from 'vscode'; +import { ExtensionContext, Uri } from 'vscode'; import { LanguageClientOptions } from 'vscode-languageclient'; import { startClient, LanguageClientConstructor } from '../jsonClient'; import { LanguageClient } from 'vscode-languageclient/browser'; @@ -17,9 +17,9 @@ declare function fetch(uri: string, options: any): any; // this method is called when vs code is activated export function activate(context: ExtensionContext) { - const serverMain = context.asAbsolutePath('server/dist/browser/jsonServerMain.js'); + const serverMain = Uri.joinPath(context.extensionUri, 'server/dist/browser/cssServerMain.js'); try { - const worker = new Worker(serverMain); + const worker = new Worker(serverMain.toString()); const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => { return new LanguageClient(id, name, clientOptions, worker); }; diff --git a/extensions/typescript-language-features/src/extension.browser.ts b/extensions/typescript-language-features/src/extension.browser.ts index 9291e22ae36..6a0f97241ea 100644 --- a/extensions/typescript-language-features/src/extension.browser.ts +++ b/extensions/typescript-language-features/src/extension.browser.ts @@ -52,7 +52,7 @@ export function activate( const versionProvider = new StaticVersionProvider( new TypeScriptVersion( TypeScriptVersionSource.Bundled, - context.asAbsolutePath('dist/browser/typescript-web/tsserver.web.js'), + vscode.Uri.joinPath(context.extensionUri, 'dist/browser/typescript-web/tsserver.web.js').toString(), API.v400)); const lazyClientHost = createLazyClientHost(context, false, { @@ -78,4 +78,3 @@ export function activate( return getExtensionApi(onCompletionAccepted.event, pluginManager); } - From 5b8aeeb1f0190e5a2fbe6b0e8ee0e844d19d2617 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 28 Aug 2020 15:00:52 +0200 Subject: [PATCH 659/736] copy paste is hard... --- .../html-language-features/client/src/browser/htmlClientMain.ts | 2 +- .../json-language-features/client/src/browser/jsonClientMain.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/html-language-features/client/src/browser/htmlClientMain.ts b/extensions/html-language-features/client/src/browser/htmlClientMain.ts index 2ca84927f2d..425dfcd6609 100644 --- a/extensions/html-language-features/client/src/browser/htmlClientMain.ts +++ b/extensions/html-language-features/client/src/browser/htmlClientMain.ts @@ -17,7 +17,7 @@ declare const TextDecoder: { // this method is called when vs code is activated export function activate(context: ExtensionContext) { - const serverMain = Uri.joinPath(context.extensionUri, 'server/dist/browser/cssServerMain.js'); + const serverMain = Uri.joinPath(context.extensionUri, 'server/dist/browser/htmlServerMain.js'); try { const worker = new Worker(serverMain.toString()); const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => { diff --git a/extensions/json-language-features/client/src/browser/jsonClientMain.ts b/extensions/json-language-features/client/src/browser/jsonClientMain.ts index 18115fa175a..6389dafb5ba 100644 --- a/extensions/json-language-features/client/src/browser/jsonClientMain.ts +++ b/extensions/json-language-features/client/src/browser/jsonClientMain.ts @@ -17,7 +17,7 @@ declare function fetch(uri: string, options: any): any; // this method is called when vs code is activated export function activate(context: ExtensionContext) { - const serverMain = Uri.joinPath(context.extensionUri, 'server/dist/browser/cssServerMain.js'); + const serverMain = Uri.joinPath(context.extensionUri, 'server/dist/browser/jsonServerMain.js'); try { const worker = new Worker(serverMain.toString()); const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => { From 489672365ef6fc7f929b1fb1f0508e2cfd411fe5 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 28 Aug 2020 15:07:25 +0200 Subject: [PATCH 660/736] Expose the contribution.menus for Variable view fixes #70377 --- .../workbench/api/common/menusExtensionPoint.ts | 7 ++++++- .../contrib/debug/browser/debugToolBar.ts | 2 -- .../contrib/debug/browser/variablesView.ts | 15 ++++++++++++++- .../workbench/contrib/debug/common/debugModel.ts | 16 ++++++++++++++++ 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/api/common/menusExtensionPoint.ts b/src/vs/workbench/api/common/menusExtensionPoint.ts index 25e3d1e692d..445d2b49ba4 100644 --- a/src/vs/workbench/api/common/menusExtensionPoint.ts +++ b/src/vs/workbench/api/common/menusExtensionPoint.ts @@ -61,7 +61,12 @@ const apiMenus: IAPIMenu[] = [ { key: 'debug/callstack/context', id: MenuId.DebugCallStackContext, - description: localize('menus.debugCallstackContext', "The debug callstack context menu") + description: localize('menus.debugCallstackContext', "The debug callstack view context menu") + }, + { + key: 'debug/variables/context', + id: MenuId.DebugVariablesContext, + description: localize('menus.debugVariablesContext', "The debug variables view context menu") }, { key: 'debug/toolBar', diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index 54c7593b579..7f60e726eef 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -21,7 +21,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { registerThemingParticipant, IThemeService, Themable } from 'vs/platform/theme/common/themeService'; import { registerColor, contrastBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; import { localize } from 'vs/nls'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -58,7 +57,6 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { @IThemeService themeService: IThemeService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IMenuService menuService: IMenuService, - @IContextMenuService contextMenuService: IContextMenuService, @IContextKeyService contextKeyService: IContextKeyService ) { super(themeService); diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index 16b982c463a..6059c435dd1 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -34,6 +34,8 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { withUndefinedAsNull } from 'vs/base/common/types'; +import { IMenuService, IMenu, MenuId } from 'vs/platform/actions/common/actions'; +import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; const $ = dom.$; let forgetScopes = true; @@ -47,6 +49,7 @@ export class VariablesView extends ViewPane { private tree!: WorkbenchAsyncDataTree; private savedViewState = new Map(); private autoExpandedScopes = new Set(); + private menu: IMenu; constructor( options: IViewletViewOptions, @@ -61,9 +64,13 @@ export class VariablesView extends ViewPane { @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, @ITelemetryService telemetryService: ITelemetryService, + @IMenuService menuService: IMenuService ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); + this.menu = menuService.createMenu(MenuId.DebugVariablesContext, contextKeyService); + this._register(this.menu); + // Use scheduler to prevent unnecessary flashing this.onFocusStackFrameScheduler = new RunOnceScheduler(async () => { const stackFrame = this.debugService.getViewModel().focusedStackFrame; @@ -213,11 +220,17 @@ export class VariablesView extends ViewPane { } } + const context = { + container: (variable.parent as (Variable | Scope)).toDebugProtocolObject(), + variable: variable.toDebugProtocolObject() + }; + const actionsDisposable = createAndFillInContextMenuActions(this.menu, { arg: context, shouldForwardArgs: false }, actions, this.contextMenuService); + this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, getActions: () => actions, getActionsContext: () => variable, - onHide: () => dispose(actions) + onHide: () => dispose(actionsDisposable) }); } } diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 1d302de30e3..5ba4e274600 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -247,6 +247,14 @@ export class Variable extends ExpressionContainer implements IExpression { toString(): string { return `${this.name}: ${this.value}`; } + + toDebugProtocolObject(): DebugProtocol.Variable { + return { + name: this.name, + variablesReference: this.reference || 0, + value: this.value + }; + } } export class Scope extends ExpressionContainer implements IScope { @@ -267,6 +275,14 @@ export class Scope extends ExpressionContainer implements IScope { toString(): string { return this.name; } + + toDebugProtocolObject(): DebugProtocol.Scope { + return { + name: this.name, + variablesReference: this.reference || 0, + expensive: this.expensive + }; + } } export class ErrorScope extends Scope { From 6a112d18937eeddf90d191cc8463da4f7ea81a54 Mon Sep 17 00:00:00 2001 From: mjcrouch <49367953+mjcrouch@users.noreply.github.com> Date: Fri, 28 Aug 2020 14:25:03 +0100 Subject: [PATCH 661/736] Clear list of created groups once they are added This prevents them being re-added again next time fixes #105483 --- src/vs/workbench/api/common/extHostSCM.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/api/common/extHostSCM.ts b/src/vs/workbench/api/common/extHostSCM.ts index 77d142d2bd6..83739e744dd 100644 --- a/src/vs/workbench/api/common/extHostSCM.ts +++ b/src/vs/workbench/api/common/extHostSCM.ts @@ -516,6 +516,7 @@ class ExtHostSourceControl implements vscode.SourceControl { } this._proxy.$registerGroups(this.handle, groups, splices); + this.createdResourceGroups.clear(); } @debounce(100) From a8603d43acf7b14dc0c745b8dec2b39502994944 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 28 Aug 2020 16:40:47 +0200 Subject: [PATCH 662/736] Fix #102823 --- extensions/docker/package.json | 2 +- .../platform/product/common/productService.ts | 4 +- .../browser/fileBasedRecommendations.ts | 108 +++++++++++------- 3 files changed, 71 insertions(+), 43 deletions(-) diff --git a/extensions/docker/package.json b/extensions/docker/package.json index 3af7727a6b4..a1cc782d213 100644 --- a/extensions/docker/package.json +++ b/extensions/docker/package.json @@ -15,7 +15,7 @@ "extensions": [ ".dockerfile", ".containerfile" ], "filenames": [ "Dockerfile", "Containerfile" ], "filenamePatterns": [ "Dockerfile.*", "Containerfile.*" ], - "aliases": [ "Dockerfile", "Containerfile" ], + "aliases": [ "Docker", "Dockerfile", "Containerfile" ], "configuration": "./language-configuration.json" }], "grammars": [{ diff --git a/src/vs/platform/product/common/productService.ts b/src/vs/platform/product/common/productService.ts index 040c869d94c..d1cb00a6d63 100644 --- a/src/vs/platform/product/common/productService.ts +++ b/src/vs/platform/product/common/productService.ts @@ -71,7 +71,7 @@ export interface IProductConfiguration { }; readonly extensionTips?: { [id: string]: string; }; - readonly extensionImportantTips?: { [id: string]: { name: string; pattern: string; isExtensionPack?: boolean }; }; + readonly extensionImportantTips?: IStringDictionary; readonly configBasedExtensionTips?: { [id: string]: IConfigBasedExtensionTip; }; readonly exeBasedExtensionTips?: { [id: string]: IExeBasedExtensionTip; }; readonly remoteExtensionTips?: { [remoteName: string]: IRemoteExtensionTip; }; @@ -127,6 +127,8 @@ export interface IProductConfiguration { readonly 'configurationSync.store'?: ConfigurationSyncStore; } +export type ImportantExtensionTip = { name: string; languages?: string[]; pattern?: string; isExtensionPack?: boolean }; + export interface IAppCenterConfiguration { readonly 'win32-ia32': string; readonly 'win32-x64': string; diff --git a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts index 0b1024e6eac..aafed5a313f 100644 --- a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts @@ -13,11 +13,11 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { localize } from 'vs/nls'; import { StorageScope, IStorageService } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IProductService } from 'vs/platform/product/common/productService'; +import { ImportantExtensionTip, IProductService } from 'vs/platform/product/common/productService'; import { forEach, IStringDictionary } from 'vs/base/common/collections'; import { ITextModel } from 'vs/editor/common/model'; import { Schemas } from 'vs/base/common/network'; -import { extname } from 'vs/base/common/resources'; +import { basename, extname } from 'vs/base/common/resources'; import { match } from 'vs/base/common/glob'; import { URI } from 'vs/base/common/uri'; import { MIME_UNKNOWN, guessMimeTypes } from 'vs/base/common/mime'; @@ -27,6 +27,7 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { setImmediate } from 'vs/base/common/platform'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IModeService } from 'vs/editor/common/services/modeService'; type FileExtensionSuggestionClassification = { userReaction: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; @@ -36,15 +37,17 @@ type FileExtensionSuggestionClassification = { const recommendationsStorageKey = 'extensionsAssistant/recommendations'; const searchMarketplace = localize('searchMarketplace', "Search Marketplace"); const milliSecondsInADay = 1000 * 60 * 60 * 24; -const processedFileExtensions: string[] = []; export class FileBasedRecommendations extends ExtensionRecommendations { private readonly extensionTips = new Map(); - private readonly importantExtensionTips = new Map(); + private readonly importantExtensionTips = new Map(); private readonly fileBasedRecommendationsByPattern = new Map(); + private readonly fileBasedRecommendationsByLanguage = new Map(); private readonly fileBasedRecommendations = new Map(); + private readonly processedFileExtensions: string[] = []; + private readonly processedLanguages: string[] = []; get recommendations(): ReadonlyArray { const recommendations: ExtensionRecommendation[] = []; @@ -90,6 +93,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { @IExtensionService private readonly extensionService: IExtensionService, @IViewletService private readonly viewletService: IViewletService, @IModelService private readonly modelService: IModelService, + @IModeService private readonly modeService: IModeService, @IProductService productService: IProductService, @IInstantiationService instantiationService: IInstantiationService, @IConfigurationService configurationService: IConfigurationService, @@ -109,6 +113,8 @@ export class FileBasedRecommendations extends ExtensionRecommendations { } protected async doActivate(): Promise { + await this.extensionService.whenInstalledExtensionsRegistered(); + const allRecommendations: string[] = []; // group extension recommendations by pattern, like {**/*.md} -> [ext.foo1, ext.bar2] @@ -119,9 +125,18 @@ export class FileBasedRecommendations extends ExtensionRecommendations { allRecommendations.push(extensionId); } for (const [extensionId, value] of this.importantExtensionTips) { - const ids = this.fileBasedRecommendationsByPattern.get(value.pattern) || []; - ids.push(extensionId); - this.fileBasedRecommendationsByPattern.set(value.pattern, ids); + if (value.pattern) { + const ids = this.fileBasedRecommendationsByPattern.get(value.pattern) || []; + ids.push(extensionId); + this.fileBasedRecommendationsByPattern.set(value.pattern, ids); + } + if (value.languages) { + for (const language of value.languages) { + const ids = this.fileBasedRecommendationsByLanguage.get(language) || []; + ids.push(extensionId); + this.fileBasedRecommendationsByLanguage.set(language, ids); + } + } allRecommendations.push(extensionId); } @@ -135,8 +150,13 @@ export class FileBasedRecommendations extends ExtensionRecommendations { } }); - this._register(this.modelService.onModelAdded(this.promptRecommendationsForModel, this)); - this.modelService.getModels().forEach(model => this.promptRecommendationsForModel(model)); + this._register(this.modelService.onModelAdded(model => this.onModelAdded(model))); + this.modelService.getModels().forEach(model => this.onModelAdded(model)); + } + + private onModelAdded(model: ITextModel): void { + this.promptRecommendationsForModel(model); + this._register(model.onDidChangeLanguage(() => this.promptRecommendationsForModel(model))); } /** @@ -146,46 +166,55 @@ export class FileBasedRecommendations extends ExtensionRecommendations { private promptRecommendationsForModel(model: ITextModel): void { const uri = model.uri; const supportedSchemes = [Schemas.untitled, Schemas.file, Schemas.vscodeRemote]; - if (!uri || supportedSchemes.indexOf(uri.scheme) === -1) { + if (!uri || !supportedSchemes.includes(uri.scheme)) { return; } - let fileExtension = extname(uri); - if (fileExtension) { - if (processedFileExtensions.indexOf(fileExtension) > -1) { - return; - } - processedFileExtensions.push(fileExtension); + const language = model.getLanguageIdentifier().language; + const fileExtension = extname(uri); + if (this.processedLanguages.includes(language) && this.processedFileExtensions.includes(fileExtension)) { + return; } + this.processedLanguages.push(language); + this.processedFileExtensions.push(fileExtension); + // re-schedule this bit of the operation to be off the critical path - in case glob-match is slow - setImmediate(() => this.promptRecommendations(uri, fileExtension)); + setImmediate(() => this.promptRecommendations(uri, language, fileExtension)); } - private async promptRecommendations(uri: URI, fileExtension: string): Promise { - const recommendationsToPrompt: { extensionId: string, languageName: string }[] = []; - for (const { 0: pattern, 1: extensionIds } of this.fileBasedRecommendationsByPattern) { + private async promptRecommendations(uri: URI, language: string, fileExtension: string): Promise { + const importantRecommendations: string[] = (this.fileBasedRecommendationsByLanguage.get(language) || []).filter(extensionId => this.importantExtensionTips.has(extensionId)); + let languageName: string | null = importantRecommendations.length ? this.modeService.getLanguageName(language) : null; + + const fileBasedRecommendations: string[] = [...importantRecommendations]; + for (let [pattern, extensionIds] of this.fileBasedRecommendationsByPattern) { + extensionIds = extensionIds.filter(extensionId => !importantRecommendations.includes(extensionId)); + if (!extensionIds.length) { + continue; + } if (!match(pattern, uri.toString())) { continue; } for (const extensionId of extensionIds) { - // Add to recommendation to prompt if it is an important tip - // Only prompt if the pattern matches the extensionImportantTips pattern - // Otherwise, assume pattern is from extensionTips, which means it should be a file based "passive" recommendation - if (this.importantExtensionTips.get(extensionId)?.pattern === pattern) { - recommendationsToPrompt.push({ extensionId, languageName: this.importantExtensionTips.get(extensionId)!.name }); + fileBasedRecommendations.push(extensionId); + const importantExtensionTip = this.importantExtensionTips.get(extensionId); + if (importantExtensionTip && importantExtensionTip.pattern === pattern) { + importantRecommendations.push(extensionId); } - - // Update file based recommendations - const filedBasedRecommendation = this.fileBasedRecommendations.get(extensionId) || { recommendedTime: Date.now(), sources: [] }; - filedBasedRecommendation.recommendedTime = Date.now(); - if (!filedBasedRecommendation.sources.some(s => s instanceof URI && s.toString() === uri.toString())) { - filedBasedRecommendation.sources.push(uri); - } - this.fileBasedRecommendations.set(extensionId, filedBasedRecommendation); } } + // Update file based recommendations + for (const recommendation of fileBasedRecommendations) { + const filedBasedRecommendation = this.fileBasedRecommendations.get(recommendation) || { recommendedTime: Date.now(), sources: [] }; + filedBasedRecommendation.recommendedTime = Date.now(); + if (!filedBasedRecommendation.sources.some(s => s instanceof URI && s.toString() === uri.toString())) { + filedBasedRecommendation.sources.push(uri); + } + this.fileBasedRecommendations.set(recommendation, filedBasedRecommendation); + } + this.storeCachedRecommendations(); if (this.hasToIgnoreRecommendationNotifications()) { @@ -193,19 +222,16 @@ export class FileBasedRecommendations extends ExtensionRecommendations { } const installed = await this.extensionsWorkbenchService.queryLocal(); - if (recommendationsToPrompt.length && - await this.promptRecommendedExtensionForFileType(fileExtension.substring(1), recommendationsToPrompt[0].languageName, recommendationsToPrompt.map(r => r.extensionId), installed)) { + if (importantRecommendations.length && + await this.promptRecommendedExtensionForFileType(languageName || basename(uri), importantRecommendations, installed)) { return; } - if (fileExtension) { - fileExtension = fileExtension.substr(1); // Strip the dot - } + fileExtension = fileExtension.substr(1); // Strip the dot if (!fileExtension) { return; } - await this.extensionService.whenInstalledExtensionsRegistered(); const mimeTypes = guessMimeTypes(uri); if (mimeTypes.length !== 1 || mimeTypes[0] !== MIME_UNKNOWN) { return; @@ -214,7 +240,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { this.promptRecommendedExtensionForFileExtension(fileExtension, installed); } - private async promptRecommendedExtensionForFileType(ext: string, languageName: string, recommendations: string[], installed: IExtension[]): Promise { + private async promptRecommendedExtensionForFileType(name: string, recommendations: string[], installed: IExtension[]): Promise { recommendations = this.filterIgnoredOrNotAllowed(recommendations); if (recommendations.length === 0) { @@ -232,7 +258,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { return false; } - this.promptImportantExtensionsInstallNotification([extensionId], localize('reallyRecommended', "Do you want to install the recommended extensions for {0}?", languageName), `@id:${extensionId}`); + this.promptImportantExtensionsInstallNotification([extensionId], localize('reallyRecommended', "Do you want to install the recommended extensions for {0}?", name), `@id:${extensionId}`); return true; } From b4216c0fc28ccc84b577f2d5961749d440656e3a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 28 Aug 2020 16:42:11 +0200 Subject: [PATCH 663/736] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 65ae6ee27f3..60fa95f8e82 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.49.0", - "distro": "0891ad485f85afec1bf016d33948b877f5562476", + "distro": "67d5dac2cd17a3fa570823005d6bc33cbc67b7b5", "author": { "name": "Microsoft Corporation" }, From e8817784f21a02b6f9c7513c7678f0c6966eaf2c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 28 Aug 2020 17:01:02 +0200 Subject: [PATCH 664/736] themes - distinguish between OS HC and autodetect HC --- src/vs/code/electron-browser/workbench/workbench.js | 6 ++++-- src/vs/code/electron-main/window.ts | 7 ++----- src/vs/platform/windows/common/windows.ts | 1 + 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index 619669fafca..5a5cb8bd6e6 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -77,6 +77,7 @@ bootstrapWindow.load([ * @param {{ * partsSplashPath?: string, * highContrast?: boolean, + * autoDetectHighContrast?: boolean, * extensionDevelopmentPath?: string[], * folderUri?: object, * workspace?: object @@ -95,7 +96,8 @@ function showPartsSplash(configuration) { } // high contrast mode has been turned on from the outside, e.g. OS -> ignore stored colors and layouts - if (data && configuration.highContrast && data.baseTheme !== 'hc-black') { + const isHighContrast = configuration.highContrast && configuration.autoDetectHighContrast; + if (data && isHighContrast && data.baseTheme !== 'hc-black') { data = undefined; } @@ -110,7 +112,7 @@ function showPartsSplash(configuration) { baseTheme = data.baseTheme; shellBackground = data.colorInfo.editorBackground; shellForeground = data.colorInfo.foreground; - } else if (configuration.highContrast) { + } else if (isHighContrast) { baseTheme = 'hc-black'; shellBackground = '#000000'; shellForeground = '#FFFFFF'; diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 48fde1abec3..2cd9d04f791 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -774,11 +774,8 @@ export class CodeWindow extends Disposable implements ICodeWindow { windowConfiguration.fullscreen = this.isFullScreen; // Set Accessibility Config - let autoDetectHighContrast = true; - if (windowConfig?.autoDetectHighContrast === false) { - autoDetectHighContrast = false; - } - windowConfiguration.highContrast = (isWindows || isMacintosh) && autoDetectHighContrast && nativeTheme.shouldUseInvertedColorScheme; + windowConfiguration.highContrast = nativeTheme.shouldUseInvertedColorScheme || nativeTheme.shouldUseHighContrastColors; + windowConfiguration.autoDetectHighContrast = windowConfig?.autoDetectHighContrast ?? true; windowConfiguration.accessibilitySupport = app.accessibilitySupportEnabled; // Title style related diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 0bf273f83f7..77b74e12c61 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -209,6 +209,7 @@ export interface IWindowConfiguration { remoteAuthority?: string; highContrast?: boolean; + autoDetectHighContrast?: boolean; filesToOpenOrCreate?: IPath[]; filesToDiff?: IPath[]; From 8a3a3d853ff36d96b65731e0b9b01e7033077496 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 28 Aug 2020 17:21:20 +0200 Subject: [PATCH 665/736] HC theme not kept when starting VSCode with HC theme enabled in OS. Fixes #105489 --- src/vs/workbench/electron-browser/window.ts | 18 +++----- .../themes/browser/workbenchThemeService.ts | 41 +++++++++++++++---- .../themes/common/workbenchThemeService.ts | 1 + 3 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 1c2fa2b665f..72ca9a6ab07 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -13,9 +13,9 @@ import { IFileService } from 'vs/platform/files/common/files'; import { toResource, IUntitledTextResourceEditorInput, SideBySideEditor, pathsToEditors } from 'vs/workbench/common/editor'; import { IEditorService, IResourceEditorInputType } from 'vs/workbench/services/editor/common/editorService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IWindowSettings, IOpenFileRequest, IWindowsConfiguration, getTitleBarStyle, IAddFoldersRequest, IDesktopRunActionInWindowRequest, IDesktopRunKeybindingInWindowRequest, IDesktopOpenFileRequest } from 'vs/platform/windows/common/windows'; +import { IOpenFileRequest, IWindowsConfiguration, getTitleBarStyle, IAddFoldersRequest, IDesktopRunActionInWindowRequest, IDesktopRunKeybindingInWindowRequest, IDesktopOpenFileRequest } from 'vs/platform/windows/common/windows'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; -import { IWorkbenchThemeService, VS_HC_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { applyZoom } from 'vs/platform/windows/electron-sandbox/window'; import { setFullscreen, getZoomLevel } from 'vs/base/browser/browser'; import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -201,19 +201,13 @@ export class NativeWindow extends Disposable { // High Contrast Events ipcRenderer.on('vscode:enterHighContrast', async () => { - const windowConfig = this.configurationService.getValue('window'); - if (windowConfig?.autoDetectHighContrast) { - await this.lifecycleService.when(LifecyclePhase.Ready); - this.themeService.setColorTheme(VS_HC_THEME, undefined); - } + await this.lifecycleService.when(LifecyclePhase.Ready); + this.themeService.setOSHighContrast(true); }); ipcRenderer.on('vscode:leaveHighContrast', async () => { - const windowConfig = this.configurationService.getValue('window'); - if (windowConfig?.autoDetectHighContrast) { - await this.lifecycleService.when(LifecyclePhase.Ready); - this.themeService.restoreColorTheme(); - } + await this.lifecycleService.when(LifecyclePhase.Ready); + this.themeService.setOSHighContrast(false); }); // keyboard layout changed event diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index 3e5529f99c5..fb6173c005e 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -91,12 +91,15 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { private readonly onProductIconThemeChange: Emitter; private readonly productIconThemeWatcher: ThemeFileWatcher; + private isOSInHighContrast: boolean; // tracking the high contrast state of the OS eventauilly should go out to a seperate service + private themeSettingIdBeforeSchemeSwitch: string | undefined; + constructor( @IExtensionService extensionService: IExtensionService, @IStorageService private readonly storageService: IStorageService, @IConfigurationService private readonly configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, + @IWorkbenchEnvironmentService readonly environmentService: IWorkbenchEnvironmentService, @IFileService private readonly fileService: IFileService, @IExtensionResourceLoaderService private readonly extensionResourceLoaderService: IExtensionResourceLoaderService, @IWorkbenchLayoutService readonly layoutService: IWorkbenchLayoutService, @@ -105,6 +108,8 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.container = layoutService.container; this.settings = new ThemeConfiguration(configurationService); + this.isOSInHighContrast = !!environmentService.configuration.highContrast; + this.colorThemeRegistry = new ThemeRegistry(extensionService, colorThemesExtPoint, ColorThemeData.fromExtensionTheme); this.colorThemeWatcher = new ThemeFileWatcher(fileService, environmentService, this.reloadCurrentColorTheme.bind(this)); this.onColorThemeChange = new Emitter({ leakWarningThreshold: 400 }); @@ -124,8 +129,11 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { // themes are loaded asynchronously, we need to initialize // a color theme document with good defaults until the theme is loaded let themeData: ColorThemeData | undefined = ColorThemeData.fromStorageData(this.storageService); - if (environmentService.configuration.highContrast && themeData?.baseTheme !== HIGH_CONTRAST) { - themeData = ColorThemeData.createUnloadedThemeForThemeType(HIGH_CONTRAST); + + // the preferred color scheme (high contrast, light, dark) has changed since the last start + const preferredColorScheme = this.getPreferredColorScheme(); + if (preferredColorScheme && themeData?.baseTheme !== preferredColorScheme && this.storageService.get(PERSISTED_OS_COLOR_SCHEME, StorageScope.GLOBAL) !== preferredColorScheme) { + themeData = ColorThemeData.createUnloadedThemeForThemeType(preferredColorScheme); } if (!themeData) { const initialColorTheme = environmentService.options?.initialColorTheme; @@ -167,9 +175,8 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } const theme = await this.colorThemeRegistry.findThemeBySettingsId(this.settings.colorTheme, DEFAULT_COLOR_THEME_ID); - const persistedColorScheme = this.storageService.get(PERSISTED_OS_COLOR_SCHEME, StorageScope.GLOBAL); const preferredColorScheme = this.getPreferredColorScheme(); - if (persistedColorScheme && preferredColorScheme && persistedColorScheme !== preferredColorScheme) { + if (preferredColorScheme && theme?.type !== preferredColorScheme) { return this.applyPreferredColorTheme(preferredColorScheme); } return this.setColorTheme(theme && theme.id, undefined); @@ -201,7 +208,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (e.affectsConfiguration(ThemeSettings.COLOR_THEME)) { this.restoreColorTheme(); } - if (e.affectsConfiguration(ThemeSettings.DETECT_COLOR_SCHEME)) { + if (e.affectsConfiguration(ThemeSettings.DETECT_COLOR_SCHEME) || e.affectsConfiguration(ThemeSettings.DETECT_HC)) { this.handlePreferredSchemeUpdated(); } if (e.affectsConfiguration(ThemeSettings.PREFERRED_DARK_THEME) && this.getPreferredColorScheme() === DARK) { @@ -315,18 +322,37 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { window.matchMedia('(prefers-color-scheme: dark)').addListener(async () => this.handlePreferredSchemeUpdated()); } + public setOSHighContrast(highContrast: boolean): void { + if (this.isOSInHighContrast !== highContrast) { + this.isOSInHighContrast = highContrast; + this.handlePreferredSchemeUpdated(); + } + } + private async handlePreferredSchemeUpdated() { const scheme = this.getPreferredColorScheme(); + + const prevScheme = this.storageService.get(PERSISTED_OS_COLOR_SCHEME, StorageScope.GLOBAL); this.storageService.store(PERSISTED_OS_COLOR_SCHEME, scheme, StorageScope.GLOBAL); if (scheme) { + if (!prevScheme) { + // remember the theme before scheme switching + this.themeSettingIdBeforeSchemeSwitch = this.settings.colorTheme; + } return this.applyPreferredColorTheme(scheme); + } else if (prevScheme && this.themeSettingIdBeforeSchemeSwitch) { + // reapply the theme before scheme switching + const theme = await this.colorThemeRegistry.findThemeBySettingsId(this.themeSettingIdBeforeSchemeSwitch, undefined); + if (theme) { + this.setColorTheme(theme.id, 'auto'); + } } return undefined; } private getPreferredColorScheme(): ThemeType | undefined { const detectHCThemeSetting = this.configurationService.getValue(ThemeSettings.DETECT_HC); - if (this.environmentService.configuration.highContrast && detectHCThemeSetting) { + if (this.isOSInHighContrast && detectHCThemeSetting) { return HIGH_CONTRAST; } if (this.configurationService.getValue(ThemeSettings.DETECT_COLOR_SCHEME)) { @@ -404,7 +430,6 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return false; } - private updateDynamicCSSRules(themeData: IColorTheme) { const cssRules = new Set(); const ruleCollector = { diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index 00bd2d9f1cf..cf14976f6fa 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -78,6 +78,7 @@ export interface IWorkbenchThemeService extends IThemeService { getProductIconThemes(): Promise; onDidProductIconThemeChange: Event; + setOSHighContrast(highContrast: boolean): void; } export interface IColorCustomizations { From c393e6a3a9e855d9cd0fb34199578923e6e936cb Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 28 Aug 2020 17:21:45 +0200 Subject: [PATCH 666/736] debt - use some "import type" syntax to ensure the module is not getting loaded --- src/vs/base/parts/storage/node/storage.ts | 2 +- .../test/electron-main/nativeHelpers.test.ts | 5 ++--- .../credentials/node/credentialsService.ts | 4 ++-- src/vs/workbench/electron-browser/window.ts | 2 +- .../electron-browser/accessibilityService.ts | 22 +++++++++---------- .../services/extensions/node/proxyResolver.ts | 5 ++--- 6 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/vs/base/parts/storage/node/storage.ts b/src/vs/base/parts/storage/node/storage.ts index 58a809a1c36..d6eca3ac03f 100644 --- a/src/vs/base/parts/storage/node/storage.ts +++ b/src/vs/base/parts/storage/node/storage.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Database, Statement } from 'vscode-sqlite3'; +import type { Database, Statement } from 'vscode-sqlite3'; import { Event } from 'vs/base/common/event'; import { timeout } from 'vs/base/common/async'; import { mapToString, setToString } from 'vs/base/common/map'; diff --git a/src/vs/code/test/electron-main/nativeHelpers.test.ts b/src/vs/code/test/electron-main/nativeHelpers.test.ts index 199fa7acaf7..1ce46448038 100644 --- a/src/vs/code/test/electron-main/nativeHelpers.test.ts +++ b/src/vs/code/test/electron-main/nativeHelpers.test.ts @@ -28,9 +28,8 @@ suite('Windows Native Helpers', () => { }); test('vscode-windows-ca-certs', async () => { - const windowsCerts = await new Promise((resolve, reject) => { - require(['vscode-windows-ca-certs'], resolve, reject); - }); + // @ts-ignore Windows only + const windowsCerts = await import('vscode-windows-ca-certs'); assert.ok(windowsCerts, 'Unable to load vscode-windows-ca-certs dependency.'); }); diff --git a/src/vs/platform/credentials/node/credentialsService.ts b/src/vs/platform/credentials/node/credentialsService.ts index cce2f77f6b3..0960eb9a542 100644 --- a/src/vs/platform/credentials/node/credentialsService.ts +++ b/src/vs/platform/credentials/node/credentialsService.ts @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import type * as keytar from 'keytar'; import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { IdleValue } from 'vs/base/common/async'; -type KeytarModule = typeof import('keytar'); export class KeytarCredentialsService implements ICredentialsService { declare readonly _serviceBrand: undefined; - private readonly _keytar = new IdleValue>(() => import('keytar')); + private readonly _keytar = new IdleValue>(() => import('keytar')); async getPassword(service: string, account: string): Promise { const keytar = await this._keytar.value; diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 72ca9a6ab07..92cc77b5b03 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -610,7 +610,7 @@ class NativeMenubarControl extends MenubarControl { @IStorageService storageService: IStorageService, @INotificationService notificationService: INotificationService, @IPreferencesService preferencesService: IPreferencesService, - @IWorkbenchEnvironmentService protected readonly environmentService: INativeWorkbenchEnvironmentService, + @IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService, @IAccessibilityService accessibilityService: IAccessibilityService, @IMenubarService private readonly menubarService: IMenubarService, @IHostService hostService: IHostService, diff --git a/src/vs/workbench/services/accessibility/electron-browser/accessibilityService.ts b/src/vs/workbench/services/accessibility/electron-browser/accessibilityService.ts index 5834d7e9023..166a53e48e1 100644 --- a/src/vs/workbench/services/accessibility/electron-browser/accessibilityService.ts +++ b/src/vs/workbench/services/accessibility/electron-browser/accessibilityService.ts @@ -41,23 +41,21 @@ export class NativeAccessibilityService extends AccessibilityService implements this.setAccessibilitySupport(environmentService.configuration.accessibilitySupport ? AccessibilitySupport.Enabled : AccessibilitySupport.Disabled); } - alwaysUnderlineAccessKeys(): Promise { + async alwaysUnderlineAccessKeys(): Promise { if (!isWindows) { - return Promise.resolve(false); + return false; } - return new Promise(async (resolve) => { - const Registry = await import('vscode-windows-registry'); + const Registry = await import('vscode-windows-registry'); - let value; - try { - value = Registry.GetStringRegKey('HKEY_CURRENT_USER', 'Control Panel\\Accessibility\\Keyboard Preference', 'On'); - } catch { - resolve(false); - } + let value: string | undefined = undefined; + try { + value = Registry.GetStringRegKey('HKEY_CURRENT_USER', 'Control Panel\\Accessibility\\Keyboard Preference', 'On'); + } catch { + return false; + } - resolve(value === '1'); - }); + return value === '1'; } setAccessibilitySupport(accessibilitySupport: AccessibilitySupport): void { diff --git a/src/vs/workbench/services/extensions/node/proxyResolver.ts b/src/vs/workbench/services/extensions/node/proxyResolver.ts index 9c1fa07fd8e..3a9f49c4471 100644 --- a/src/vs/workbench/services/extensions/node/proxyResolver.ts +++ b/src/vs/workbench/services/extensions/node/proxyResolver.ts @@ -493,9 +493,8 @@ async function readCaCertificates() { } async function readWindowsCaCertificates() { - const winCA = await new Promise((resolve, reject) => { - require(['vscode-windows-ca-certs'], resolve, reject); - }); + // @ts-ignore Windows only + const winCA = await import('vscode-windows-ca-certs'); let ders: any[] = []; const store = winCA(); From 6384e3246884694cbc34eacc70281ada15e2c7f2 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 28 Aug 2020 17:30:41 +0200 Subject: [PATCH 667/736] fix tests --- .../electron-browser/extensionRecommendationsService.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts index be5ec682bc4..0ac3774a174 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts @@ -58,6 +58,7 @@ import { NoOpWorkspaceTagsService } from 'vs/workbench/contrib/tags/browser/work import { IWorkspaceTagsService } from 'vs/workbench/contrib/tags/common/workspaceTags'; import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; const mockExtensionGallery: IGalleryExtension[] = [ aGalleryExtension('MockExtension1', { @@ -208,6 +209,9 @@ suite('ExtensionRecommendationsService Test', () => { async canInstall() { return true; }, async getExtensionsReport() { return []; }, }); + instantiationService.stub(IExtensionService, >{ + async whenInstalledExtensionsRegistered() { return true; } + }); instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(IURLService, NativeURLService); From 0ffdb5ac5116c18c81ecf2f327b3f063926c535c Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 28 Aug 2020 08:48:34 -0700 Subject: [PATCH 668/736] fix #105576. --- .../notebook/browser/contrib/fold/folding.ts | 145 ++++++++++++++---- .../browser/contrib/fold/foldingModel.ts | 71 ++++++++- 2 files changed, 187 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts b/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts index 814dc3a2527..6b24b18e9a2 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts @@ -18,6 +18,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { getActiveNotebookEditor, NOTEBOOK_ACTIONS_CATEGORY } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; import { localize } from 'vs/nls'; +import { FoldingRegion } from 'vs/editor/contrib/folding/foldingRanges'; export class FoldingController extends Disposable implements INotebookEditorContribution { static id: string = 'workbench.notebook.findController'; @@ -65,19 +66,31 @@ export class FoldingController extends Disposable implements INotebookEditorCont this._updateEditorFoldingRanges(); } - setFoldingState(index: number, state: CellFoldingState) { + setFoldingStateDown(index: number, state: CellFoldingState, levels: number) { + const doCollapse = state === CellFoldingState.Collapsed; + let region = this._foldingModel!.getRegionAtLine(index + 1); + let regions: FoldingRegion[] = []; + if (region) { + if (region.isCollapsed !== doCollapse) { + regions.push(region); + } + if (levels > 1) { + let regionsInside = this._foldingModel!.getRegionsInside(region, (r, level: number) => r.isCollapsed !== doCollapse && level < levels); + regions.push(...regionsInside); + } + } + + regions.forEach(r => this._foldingModel!.setCollapsed(r.regionIndex, state === CellFoldingState.Collapsed)); + this._updateEditorFoldingRanges(); + } + + setFoldingStateUp(index: number, state: CellFoldingState, levels: number) { if (!this._foldingModel) { return; } - const range = this._foldingModel.regions.findRange(index + 1); - const startIndex = this._foldingModel.regions.getStartLineNumber(range) - 1; - - if (startIndex !== index) { - return; - } - - this._foldingModel.setCollapsed(range, state === CellFoldingState.Collapsed); + let regions = this._foldingModel.getAllRegionsAtLine(index + 1, (region, level) => region.isCollapsed !== (state === CellFoldingState.Collapsed) && level <= levels); + regions.forEach(r => this._foldingModel!.setCollapsed(r.regionIndex, state === CellFoldingState.Collapsed)); this._updateEditorFoldingRanges(); } @@ -121,7 +134,7 @@ export class FoldingController extends Disposable implements INotebookEditorCont return; } - this.setFoldingState(modelIndex, state === CellFoldingState.Collapsed ? CellFoldingState.Expanded : CellFoldingState.Collapsed); + this.setFoldingStateUp(modelIndex, state === CellFoldingState.Collapsed ? CellFoldingState.Expanded : CellFoldingState.Collapsed, 1); } return; @@ -130,6 +143,10 @@ export class FoldingController extends Disposable implements INotebookEditorCont registerNotebookContribution(FoldingController.id, FoldingController); + +const NOTEBOOK_FOLD_COMMAND_LABEL = localize('fold.cell', "Fold Cell"); +const NOTEBOOK_UNFOLD_COMMAND_LABEL = localize('unfold.cell', "Unfold Cell"); + registerAction2(class extends Action2 { constructor() { super({ @@ -146,12 +163,39 @@ registerAction2(class extends Action2 { secondary: [KeyCode.LeftArrow], weight: KeybindingWeight.WorkbenchContrib }, + description: { + description: NOTEBOOK_FOLD_COMMAND_LABEL, + args: [ + { + name: 'index', + description: 'The cell index', + schema: { + 'type': 'object', + 'required': ['index', 'direction'], + 'properties': { + 'index': { + 'type': 'number' + }, + 'direction': { + 'type': 'string', + 'enum': ['up', 'down'], + 'default': 'down' + }, + 'levels': { + 'type': 'number', + 'default': 1 + }, + } + } + } + ] + }, precondition: NOTEBOOK_IS_ACTIVE_EDITOR, f1: true }); } - async run(accessor: ServicesAccessor): Promise { + async run(accessor: ServicesAccessor, args?: { index: number, levels: number, direction: 'up' | 'down' }): Promise { const editorService = accessor.get(IEditorService); const editor = getActiveNotebookEditor(editorService); @@ -159,17 +203,27 @@ registerAction2(class extends Action2 { return; } - const activeCell = editor.getActiveCell(); - if (!activeCell) { - return; + const levels = args && args.levels || 1; + const direction = args && args.direction === 'up' ? 'up' : 'down'; + let index: number | undefined = undefined; + + if (args) { + index = args.index; + } else { + const activeCell = editor.getActiveCell(); + if (!activeCell) { + return; + } + index = editor.viewModel?.viewCells.indexOf(activeCell); } const controller = editor.getContribution(FoldingController.id); - - const index = editor.viewModel?.viewCells.indexOf(activeCell); - if (index !== undefined) { - controller.setFoldingState(index, CellFoldingState.Collapsed); + if (direction === 'up') { + controller.setFoldingStateUp(index, CellFoldingState.Collapsed, levels); + } else { + controller.setFoldingStateDown(index, CellFoldingState.Collapsed, levels); + } } } }); @@ -178,7 +232,7 @@ registerAction2(class extends Action2 { constructor() { super({ id: 'notebook.unfold', - title: { value: localize('unfold.cell', "Unfold Cell"), original: 'Unfold Cell' }, + title: { value: NOTEBOOK_UNFOLD_COMMAND_LABEL, original: 'Unfold Cell' }, category: NOTEBOOK_ACTIONS_CATEGORY, keybinding: { when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), @@ -190,12 +244,39 @@ registerAction2(class extends Action2 { secondary: [KeyCode.RightArrow], weight: KeybindingWeight.WorkbenchContrib }, + description: { + description: NOTEBOOK_UNFOLD_COMMAND_LABEL, + args: [ + { + name: 'index', + description: 'The cell index', + schema: { + 'type': 'object', + 'required': ['index', 'direction'], + 'properties': { + 'index': { + 'type': 'number' + }, + 'direction': { + 'type': 'string', + 'enum': ['up', 'down'], + 'default': 'down' + }, + 'levels': { + 'type': 'number', + 'default': 1 + }, + } + } + } + ] + }, precondition: NOTEBOOK_IS_ACTIVE_EDITOR, f1: true }); } - async run(accessor: ServicesAccessor): Promise { + async run(accessor: ServicesAccessor, args?: { index: number, levels: number, direction: 'up' | 'down' }): Promise { const editorService = accessor.get(IEditorService); const editor = getActiveNotebookEditor(editorService); @@ -203,17 +284,27 @@ registerAction2(class extends Action2 { return; } - const activeCell = editor.getActiveCell(); - if (!activeCell) { - return; + const levels = args && args.levels || 1; + const direction = args && args.direction === 'up' ? 'up' : 'down'; + let index: number | undefined = undefined; + + if (args) { + index = args.index; + } else { + const activeCell = editor.getActiveCell(); + if (!activeCell) { + return; + } + index = editor.viewModel?.viewCells.indexOf(activeCell); } const controller = editor.getContribution(FoldingController.id); - - const index = editor.viewModel?.viewCells.indexOf(activeCell); - if (index !== undefined) { - controller.setFoldingState(index, CellFoldingState.Expanded); + if (direction === 'up') { + controller.setFoldingStateUp(index, CellFoldingState.Expanded, levels); + } else { + controller.setFoldingStateDown(index, CellFoldingState.Expanded, levels); + } } } }); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts b/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts index 18f853b05e5..fade3589aa0 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts @@ -6,12 +6,16 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { TrackedRangeStickiness } from 'vs/editor/common/model'; -import { FoldingRegions } from 'vs/editor/contrib/folding/foldingRanges'; +import { FoldingRegion, FoldingRegions } from 'vs/editor/contrib/folding/foldingRanges'; import { IFoldingRangeData, sanitizeRanges } from 'vs/editor/contrib/folding/syntaxRangeProvider'; import { ICellRange } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +type RegionFilter = (r: FoldingRegion) => boolean; +type RegionFilterWithLevel = (r: FoldingRegion, level: number) => boolean; + + export class FoldingModel extends Disposable { private _viewModel: NotebookViewModel | null = null; private _viewModelStore = new DisposableStore(); @@ -73,7 +77,70 @@ export class FoldingModel extends Disposable { this.recompute(); } - public setCollapsed(index: number, newState: boolean) { + getRegionAtLine(lineNumber: number): FoldingRegion | null { + if (this._regions) { + let index = this._regions.findRange(lineNumber); + if (index >= 0) { + return this._regions.toRegion(index); + } + } + return null; + } + + getRegionsInside(region: FoldingRegion | null, filter?: RegionFilter | RegionFilterWithLevel): FoldingRegion[] { + let result: FoldingRegion[] = []; + let index = region ? region.regionIndex + 1 : 0; + let endLineNumber = region ? region.endLineNumber : Number.MAX_VALUE; + + if (filter && filter.length === 2) { + const levelStack: FoldingRegion[] = []; + for (let i = index, len = this._regions.length; i < len; i++) { + let current = this._regions.toRegion(i); + if (this._regions.getStartLineNumber(i) < endLineNumber) { + while (levelStack.length > 0 && !current.containedBy(levelStack[levelStack.length - 1])) { + levelStack.pop(); + } + levelStack.push(current); + if (filter(current, levelStack.length)) { + result.push(current); + } + } else { + break; + } + } + } else { + for (let i = index, len = this._regions.length; i < len; i++) { + let current = this._regions.toRegion(i); + if (this._regions.getStartLineNumber(i) < endLineNumber) { + if (!filter || (filter as RegionFilter)(current)) { + result.push(current); + } + } else { + break; + } + } + } + return result; + } + + getAllRegionsAtLine(lineNumber: number, filter?: (r: FoldingRegion, level: number) => boolean): FoldingRegion[] { + let result: FoldingRegion[] = []; + if (this._regions) { + let index = this._regions.findRange(lineNumber); + let level = 1; + while (index >= 0) { + let current = this._regions.toRegion(index); + if (!filter || filter(current, level)) { + result.push(current); + } + level++; + index = current.parentIndex; + } + } + return result; + } + + setCollapsed(index: number, newState: boolean) { this._regions.setCollapsed(index, newState); } From 513807b0e44065fbfdfd85671c56fdce1b346ac9 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 28 Aug 2020 17:50:34 +0200 Subject: [PATCH 669/736] web - improve window indicator --- resources/web/code-web.js | 2 +- src/vs/code/browser/workbench/workbench.ts | 34 +++++++--------------- 2 files changed, 11 insertions(+), 25 deletions(-) diff --git a/resources/web/code-web.js b/resources/web/code-web.js index 8818b5d9efd..58f3e586539 100644 --- a/resources/web/code-web.js +++ b/resources/web/code-web.js @@ -27,7 +27,7 @@ const BUILTIN_MARKETPLACE_EXTENSIONS_ROOT = path.join(APP_ROOT, '.build', 'built const WEB_DEV_EXTENSIONS_ROOT = path.join(APP_ROOT, '.build', 'builtInWebDevExtensions'); const WEB_MAIN = path.join(APP_ROOT, 'src', 'vs', 'code', 'browser', 'workbench', 'workbench-dev.html'); -const WEB_PLAYGROUND_VERSION = '0.0.4'; +const WEB_PLAYGROUND_VERSION = '0.0.5'; const args = minimist(process.argv, { boolean: [ diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 4b0ac90a35a..fccfbe81032 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -3,8 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWorkbenchConstructionOptions, create, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IWindowIndicator, ICommand, IHomeIndicator, IProductQualityChangeHandler } from 'vs/workbench/workbench.web.api'; -import product from 'vs/platform/product/common/product'; +import { IWorkbenchConstructionOptions, create, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IWindowIndicator, IHomeIndicator, IProductQualityChangeHandler } from 'vs/workbench/workbench.web.api'; import { URI, UriComponents } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; import { generateUuid } from 'vs/base/common/uuid'; @@ -302,8 +301,6 @@ class WindowIndicator implements IWindowIndicator { readonly tooltip: string; readonly command: string | undefined; - readonly commandImpl: ICommand | undefined = undefined; - constructor(workspace: IWorkspace) { let repositoryOwner: string | undefined = undefined; let repositoryName: string | undefined = undefined; @@ -321,20 +318,16 @@ class WindowIndicator implements IWindowIndicator { } } + // Repo if (repositoryName && repositoryOwner) { - this.label = localize('openInDesktopLabel', "$(remote) Open in Desktop"); - this.tooltip = localize('openInDesktopTooltip', "Open in Desktop"); - this.command = '_web.openInDesktop'; - this.commandImpl = { - id: this.command, - handler: () => { - const protocol = product.quality === 'stable' ? 'vscode' : 'vscode-insiders'; - window.open(`${protocol}://vscode.git/clone?url=${encodeURIComponent(`https://github.com/${repositoryOwner}/${repositoryName}.git`)}`); - } - }; - } else { - this.label = localize('playgroundLabel', "Web Playground"); - this.tooltip = this.label; + this.label = localize('playgroundLabelRepository', "$(remote) VS Code Web Playground: {0}/{1}", repositoryOwner, repositoryName); + this.tooltip = localize('playgroundRepositoryTooltip', "VS Code Web Playground: {0}/{1}", repositoryOwner, repositoryName); + } + + // No Repo + else { + this.label = localize('playgroundLabel', "$(remote) VS Code Web Playground"); + this.tooltip = localize('playgroundTooltip', "VS Code Web Playground"); } } } @@ -416,16 +409,10 @@ class WindowIndicator implements IWindowIndicator { title: localize('home', "Home") }; - // Commands - const commands: ICommand[] = []; - // Window indicator (unless connected to a remote) let windowIndicator: WindowIndicator | undefined = undefined; if (!workspaceProvider.hasRemote()) { windowIndicator = new WindowIndicator(workspace); - if (windowIndicator.commandImpl) { - commands.push(windowIndicator.commandImpl); - } } // Product Quality Change Handler @@ -447,7 +434,6 @@ class WindowIndicator implements IWindowIndicator { create(document.body, { ...config, homeIndicator, - commands, windowIndicator, productQualityChangeHandler, workspaceProvider, From 208fe75f0cffcaae55bf25423432a326a86edf62 Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 28 Aug 2020 08:51:48 -0700 Subject: [PATCH 670/736] show revert only in diff editor. --- .../workbench/contrib/notebook/browser/diff/cellComponents.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index d831793d8b5..2c938b1c3ae 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -291,7 +291,7 @@ abstract class AbstractCellRenderer extends Disposable { { updateInfoRendering: this.updateMetadataRendering.bind(this), checkIfModified: (cell) => { - return hash(this._getFormatedMetadataJSON(cell.original?.metadata || {}, cell.original?.language)) !== hash(this._getFormatedMetadataJSON(cell.modified?.metadata ?? {}, cell.modified?.language)); + return cell.type !== 'delete' && cell.type !== 'insert' && hash(this._getFormatedMetadataJSON(cell.original?.metadata || {}, cell.original?.language)) !== hash(this._getFormatedMetadataJSON(cell.modified?.metadata ?? {}, cell.modified?.language)); }, getFoldingState: (cell) => { return cell.metadataFoldingState; @@ -319,7 +319,7 @@ abstract class AbstractCellRenderer extends Disposable { { updateInfoRendering: this.updateOutputRendering.bind(this), checkIfModified: (cell) => { - return !this.notebookEditor.textModel!.transientOptions.transientOutputs && cell.type === 'modified' && hash(cell.original?.outputs ?? []) !== hash(cell.modified?.outputs ?? []); + return cell.type !== 'delete' && cell.type !== 'insert' && !this.notebookEditor.textModel!.transientOptions.transientOutputs && cell.type === 'modified' && hash(cell.original?.outputs ?? []) !== hash(cell.modified?.outputs ?? []); }, getFoldingState: (cell) => { return this.cell.outputFoldingState; From dd3e9ee04b1db88da1301f5f5a3d02652576939d Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 28 Aug 2020 17:52:42 +0200 Subject: [PATCH 671/736] repl: cmd + f to focus filter --- src/vs/workbench/contrib/debug/browser/repl.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 85f739834b5..14d45b12304 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -96,6 +96,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { private modelChangeListener: IDisposable = Disposable.None; private filter: ReplFilter; private filterState: TreeFilterState; + private filterActionViewItem: TreeFilterPanelActionViewItem | undefined; constructor( options: IViewPaneOptions, @@ -276,8 +277,8 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { this.navigateHistory(false); } - focusRepl(): void { - this.tree.domFocus(); + focusFilter(): void { + this.filterActionViewItem?.focus(); } private setMode(): void { @@ -458,7 +459,8 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { if (action.id === SelectReplAction.ID) { return this.instantiationService.createInstance(SelectReplActionViewItem, this.selectReplAction); } else if (action.id === FILTER_ACTION_ID) { - return this.instantiationService.createInstance(TreeFilterPanelActionViewItem, action, localize('workbench.debug.filter.placeholder', "Filter. E.g.: text, !exclude"), this.filterState); + this.filterActionViewItem = this.instantiationService.createInstance(TreeFilterPanelActionViewItem, action, localize('workbench.debug.filter.placeholder', "Filter. E.g.: text, !exclude"), this.filterState); + return this.filterActionViewItem; } return super.getActionViewItem(action); @@ -764,7 +766,7 @@ class FilterReplAction extends EditorAction { run(accessor: ServicesAccessor, editor: ICodeEditor): void | Promise { SuggestController.get(editor).acceptSelectedSuggestion(false, true); const repl = getReplView(accessor.get(IViewsService)); - repl?.focusRepl(); + repl?.focusFilter(); } } From a3f414cf5abbed3c1997e8eff01f2d283a8f1ad0 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 28 Aug 2020 16:45:23 +0200 Subject: [PATCH 672/736] add some integration tests for notebook editing, https://github.com/microsoft/vscode/issues/105283 --- .../src/notebook.test.ts | 103 ++++++++++++++++-- .../api/browser/mainThreadNotebook.ts | 2 +- .../workbench/api/common/extHost.protocol.ts | 2 +- .../workbench/api/common/extHostNotebook.ts | 14 +-- 4 files changed, 104 insertions(+), 17 deletions(-) diff --git a/extensions/vscode-notebook-tests/src/notebook.test.ts b/extensions/vscode-notebook-tests/src/notebook.test.ts index c09d171fc6d..c34d11c4baf 100644 --- a/extensions/vscode-notebook-tests/src/notebook.test.ts +++ b/extensions/vscode-notebook-tests/src/notebook.test.ts @@ -70,8 +70,11 @@ async function saveFileAndCloseAll(resource: vscode.Uri) { await documentClosed; } -async function saveAllFilesAndCloseAll(resource: vscode.Uri) { +async function saveAllFilesAndCloseAll(resource: vscode.Uri | undefined) { const documentClosed = new Promise((resolve, _reject) => { + if (!resource) { + return resolve(); + } const d = vscode.notebook.onDidCloseNotebookDocument(e => { if (e.uri.toString() === resource.toString()) { d.dispose(); @@ -393,23 +396,107 @@ suite('Notebook API tests', () => { await saveFileAndCloseAll(resource); }); - test('edit API', async function () { + test('edit API (replaceCells)', async function () { assertInitalState(); const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); await vscode.notebook.activeNotebookEditor!.edit(editBuilder => { - editBuilder.insert(1, 'test 2', 'javascript', vscode.CellKind.Code, [], undefined); + editBuilder.replaceCells(1, 0, [{ cellKind: vscode.CellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]); }); const cellChangeEventRet = await cellsChangeEvent; - assert.equal(cellChangeEventRet.document, vscode.notebook.activeNotebookEditor?.document); - assert.equal(cellChangeEventRet.changes.length, 1); - assert.deepEqual(cellChangeEventRet.changes[0].start, 1); - assert.deepEqual(cellChangeEventRet.changes[0].deletedCount, 0); - assert.equal(cellChangeEventRet.changes[0].items[0], vscode.notebook.activeNotebookEditor!.document.cells[1]); + assert.strictEqual(cellChangeEventRet.document === vscode.notebook.activeNotebookEditor?.document, true); + assert.strictEqual(cellChangeEventRet.document.isDirty, true); + assert.strictEqual(cellChangeEventRet.changes.length, 1); + assert.strictEqual(cellChangeEventRet.changes[0].start, 1); + assert.strictEqual(cellChangeEventRet.changes[0].deletedCount, 0); + assert.strictEqual(cellChangeEventRet.changes[0].items[0] === vscode.notebook.activeNotebookEditor!.document.cells[1], true); + await saveAllFilesAndCloseAll(resource); + }); + + test('edit API (replaceOutput)', async function () { + // no output events yet... + this.skip(); + + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + await vscode.notebook.activeNotebookEditor!.edit(editBuilder => { + editBuilder.replaceOutput(0, [{ outputKind: vscode.CellOutputKind.Rich, data: { foo: 'bar' } }]); + }); + + const document = vscode.notebook.activeNotebookEditor?.document!; + assert.strictEqual(document.isDirty, false); + assert.strictEqual(document.cells.length, 1); + assert.strictEqual(document.cells[0].outputs.length, 1); + assert.strictEqual(document.cells[0].outputs[0].outputKind, vscode.CellOutputKind.Rich); + + await saveAllFilesAndCloseAll(undefined); + }); + + test('edit API (replaceOutput, event)', async function () { + // no output events yet... + this.skip(); + + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + const outputChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeCellOutputs); + await vscode.notebook.activeNotebookEditor!.edit(editBuilder => { + editBuilder.replaceOutput(0, [{ outputKind: vscode.CellOutputKind.Rich, data: { foo: 'bar' } }]); + }); + + const value = await outputChangeEvent; + assert.strictEqual(value.document === vscode.notebook.activeNotebookEditor?.document, true); + assert.strictEqual(value.cells.length, 1); + assert.strictEqual(value.cells[0].outputs.length, 1); + assert.strictEqual(value.cells[0].outputs[0].outputKind, vscode.CellOutputKind.Rich); + + await saveAllFilesAndCloseAll(undefined); + }); + + test('edit API (replaceMetadata)', async function () { + + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + await vscode.notebook.activeNotebookEditor!.edit(editBuilder => { + editBuilder.replaceMetadata(0, { inputCollapsed: true, executionOrder: 17 }); + }); + + const document = vscode.notebook.activeNotebookEditor?.document!; + assert.strictEqual(document.cells.length, 1); + assert.strictEqual(document.cells[0].metadata.executionOrder, 17); + assert.strictEqual(document.cells[0].metadata.inputCollapsed, true); + + assert.strictEqual(document.isDirty, true); + await saveFileAndCloseAll(resource); + }); + + test('edit API (replaceMetadata, event)', async function () { + + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + const event = getEventOncePromise(vscode.notebook.onDidChangeCellMetadata); + + await vscode.notebook.activeNotebookEditor!.edit(editBuilder => { + editBuilder.replaceMetadata(0, { inputCollapsed: true, executionOrder: 17 }); + }); + + const data = await event; + assert.strictEqual(data.document, vscode.notebook.activeNotebookEditor?.document); + assert.strictEqual(data.cell.metadata.executionOrder, 17); + assert.strictEqual(data.cell.metadata.inputCollapsed, true); + + assert.strictEqual(data.document.isDirty, true); await saveFileAndCloseAll(resource); }); diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 7b67595e6ac..dd8b3f29585 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -242,7 +242,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo const disposableStore = new DisposableStore(); const textModel = this._notebookService.getNotebookTextModel(doc); disposableStore.add(textModel!.onDidModelChangeProxy(e => { - this._proxy.$acceptModelChanged(textModel!.uri, e); + this._proxy.$acceptModelChanged(textModel!.uri, e, textModel!.isDirty); this._proxy.$acceptEditorPropertiesChanged(doc, { selections: { selections: textModel!.selections }, metadata: null }); })); disposableStore.add(textModel!.onDidSelectionChange(e => { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 2ff5e70e14a..25182aee42f 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1668,7 +1668,7 @@ export interface ExtHostNotebookShape { $acceptDisplayOrder(displayOrder: INotebookDisplayOrder): void; $acceptNotebookActiveKernelChange(event: { uri: UriComponents, providerHandle: number | undefined, kernelId: string | undefined }): void; $onDidReceiveMessage(editorId: string, rendererId: string | undefined, message: unknown): void; - $acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent): void; + $acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent, isDirty: boolean): void; $acceptModelSaved(uriComponents: UriComponents): void; $acceptEditorPropertiesChanged(uriComponents: UriComponents, data: INotebookEditorPropertiesChangeData): void; $acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta): void; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index ae6d7860f62..f9ba5b1ef75 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -213,6 +213,7 @@ export class ExtHostNotebookDocument extends Disposable { private _metadataChangeListener: IDisposable; private _displayOrder: string[] = []; private _versionId = 0; + private _isDirty: boolean = false; private _backupCounter = 1; private _backup?: vscode.NotebookDocumentBackup; private _disposed = false; @@ -274,7 +275,7 @@ export class ExtHostNotebookDocument extends Disposable { get version() { return that._versionId; }, get fileName() { return that.uri.fsPath; }, get viewType() { return that._viewType; }, - get isDirty() { return false; }, + get isDirty() { return that._isDirty; }, get isUntitled() { return that.uri.scheme === Schemas.untitled; }, get cells(): ReadonlyArray { return that._cells.map(cell => cell.cell); }, get languages() { return that._languages; }, @@ -311,8 +312,9 @@ export class ExtHostNotebookDocument extends Disposable { this._backup = undefined; } - acceptModelChanged(event: NotebookCellsChangedEvent): void { + acceptModelChanged(event: NotebookCellsChangedEvent, isDirty: boolean): void { this._versionId = event.versionId; + this._isDirty = isDirty; if (event.kind === NotebookCellsChangeType.Initialize) { this._spliceNotebookCells(event.changes, true); } if (event.kind === NotebookCellsChangeType.ModelChange) { @@ -1255,12 +1257,10 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN this._webviewComm.get(editorId)?.onDidReceiveMessage(forRendererType, message); } - $acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent): void { - + $acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent, isDirty: boolean): void { const document = this._documents.get(URI.revive(uriComponents)); - if (document) { - document.acceptModelChanged(event); + document.acceptModelChanged(event, isDirty); } } @@ -1401,7 +1401,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN 0, modelData.cells ]] - }); + }, false); // add cell document as vscode.TextDocument addedCellDocuments.push(...modelData.cells.map(cell => ExtHostCell.asModelAddData(document.notebookDocument, cell))); From 807eb3782629e2aa0c35a038f4ddd4a0ef1f9f6f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 28 Aug 2020 17:59:03 +0200 Subject: [PATCH 673/736] make sure replaceOutput updates the extension host side, update tests https://github.com/microsoft/vscode/issues/105283 --- .../src/notebook.test.ts | 9 ++----- .../workbench/api/common/extHostNotebook.ts | 12 ++++++++++ .../common/model/notebookTextModel.ts | 21 ++++++++++++---- .../contrib/notebook/common/notebookCommon.ts | 18 ++++++++++---- .../test/browser/api/extHostNotebook.test.ts | 4 ++-- .../api/extHostNotebookConcatDocument.test.ts | 24 +++++++++---------- 6 files changed, 57 insertions(+), 31 deletions(-) diff --git a/extensions/vscode-notebook-tests/src/notebook.test.ts b/extensions/vscode-notebook-tests/src/notebook.test.ts index c34d11c4baf..d549dc8dc3f 100644 --- a/extensions/vscode-notebook-tests/src/notebook.test.ts +++ b/extensions/vscode-notebook-tests/src/notebook.test.ts @@ -418,9 +418,6 @@ suite('Notebook API tests', () => { }); test('edit API (replaceOutput)', async function () { - // no output events yet... - this.skip(); - assertInitalState(); const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); @@ -430,7 +427,7 @@ suite('Notebook API tests', () => { }); const document = vscode.notebook.activeNotebookEditor?.document!; - assert.strictEqual(document.isDirty, false); + assert.strictEqual(document.isDirty, true); assert.strictEqual(document.cells.length, 1); assert.strictEqual(document.cells[0].outputs.length, 1); assert.strictEqual(document.cells[0].outputs[0].outputKind, vscode.CellOutputKind.Rich); @@ -439,9 +436,6 @@ suite('Notebook API tests', () => { }); test('edit API (replaceOutput, event)', async function () { - // no output events yet... - this.skip(); - assertInitalState(); const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); @@ -453,6 +447,7 @@ suite('Notebook API tests', () => { const value = await outputChangeEvent; assert.strictEqual(value.document === vscode.notebook.activeNotebookEditor?.document, true); + assert.strictEqual(value.document.isDirty, true); assert.strictEqual(value.cells.length, 1); assert.strictEqual(value.cells[0].outputs.length, 1); assert.strictEqual(value.cells[0].outputs[0].outputKind, vscode.CellOutputKind.Rich); diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index f9ba5b1ef75..379d6c285bd 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -139,6 +139,10 @@ export class ExtHostCell extends Disposable { this._onDidDispose.fire(); } + setOutputs(newOutputs: vscode.CellOutput[]): void { + this._outputs = newOutputs; + } + private _updateOutputs(newOutputs: vscode.CellOutput[]) { const rawDiffs = diff(this._outputs || [], newOutputs || [], (a) => { return this._outputMapping.has(a); @@ -321,6 +325,8 @@ export class ExtHostNotebookDocument extends Disposable { this._spliceNotebookCells(event.changes, false); } else if (event.kind === NotebookCellsChangeType.Move) { this._moveCell(event.index, event.newIdx); + } else if (event.kind === NotebookCellsChangeType.Output) { + this._setCellOutputs(event.index, event.outputs); } else if (event.kind === NotebookCellsChangeType.CellClearOutput) { this._clearCellOutputs(event.index); } else if (event.kind === NotebookCellsChangeType.CellsClearOutput) { @@ -412,6 +418,12 @@ export class ExtHostNotebookDocument extends Disposable { }); } + private _setCellOutputs(index: number, outputs: IProcessedOutput[]): void { + const cell = this._cells[index]; + cell.setOutputs(outputs); + this._emitter.emitCellOutputsChange({ document: this.notebookDocument, cells: [cell.cell] }); + } + private _clearCellOutputs(index: number): void { const cell = this._cells[index].cell; cell.outputs = []; diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 1f68435cbdc..c1a7165beb8 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -505,13 +505,24 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel // TODO@rebornix should this trigger content change event? spliceNotebookCellOutputs(cellHandle: number, splices: NotebookCellOutputsSplice[]): void { const cell = this._mapping.get(cellHandle); - cell?.spliceNotebookCellOutputs(splices); + if (cell) { - if (!this.transientOptions.transientOutputs) { - this._increaseVersionId(); - this.setDirty(true); - this._onDidChangeContent.fire(); + cell.spliceNotebookCellOutputs(splices); + + if (!this.transientOptions.transientOutputs) { + this._increaseVersionId(); + this.setDirty(true); + this._onDidChangeContent.fire(); + } + + this._onDidModelChangeProxy.fire({ + kind: NotebookCellsChangeType.Output, + versionId: this.versionId, + index: this.cells.indexOf(cell), + outputs: cell.outputs ?? [] + }); } + } clearCellOutput(handle: number) { diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 8f3e2d757d9..8300e9f3950 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -166,15 +166,15 @@ export interface IErrorOutput { /** * Exception Name */ - ename?: string; + ename: string; /** * Exception Value */ - evalue?: string; + evalue: string; /** * Exception call stacks */ - traceback?: string[]; + traceback: string[]; } export interface NotebookCellOutputMetadata { @@ -366,7 +366,8 @@ export enum NotebookCellsChangeType { CellsClearOutput = 4, ChangeLanguage = 5, Initialize = 6, - ChangeMetadata = 7 + ChangeMetadata = 7, + Output = 8, } export interface NotebookCellsInitializeEvent { @@ -388,6 +389,13 @@ export interface NotebookCellsModelMoveEvent { readonly versionId: number; } +export interface NotebookOutputChangedEvent { + readonly kind: NotebookCellsChangeType.Output; + readonly index: number; + readonly versionId: number; + readonly outputs: IProcessedOutput[]; +} + export interface NotebookCellClearOutputEvent { readonly kind: NotebookCellsChangeType.CellClearOutput; readonly index: number; @@ -413,7 +421,7 @@ export interface NotebookCellsChangeMetadataEvent { readonly metadata: NotebookCellMetadata | undefined; } -export type NotebookCellsChangedEvent = NotebookCellsInitializeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookCellClearOutputEvent | NotebookCellsClearOutputEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent; +export type NotebookCellsChangedEvent = NotebookCellsInitializeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookOutputChangedEvent | NotebookCellClearOutputEvent | NotebookCellsClearOutputEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent; export const enum CellEditType { Replace = 1, diff --git a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts index 42ea107dbc7..2911cf0ab90 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts @@ -178,7 +178,7 @@ suite('NotebookCell#Document', function () { cellKind: CellKind.Code, outputs: [], }]]] - }); + }, false); await p; @@ -234,7 +234,7 @@ suite('NotebookCell#Document', function () { kind: NotebookCellsChangeType.ModelChange, versionId: 2, changes: [[0, 1, []]] - }); + }, false); assert.equal(notebook.notebookDocument.cells.length, 1); assert.equal(cell1.document.isClosed, true); // ref still alive! diff --git a/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts b/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts index 764eee4540c..a25ee16c3f4 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts @@ -143,7 +143,7 @@ suite('NotebookConcatDocument', function () { cellKind: CellKind.Code, outputs: [], }]]] - }); + }, false); assert.equal(notebook.notebookDocument.cells.length, 1 + 2); // markdown and code @@ -177,7 +177,7 @@ suite('NotebookConcatDocument', function () { cellKind: CellKind.Code, outputs: [], }]]] - }); + }, false); assert.equal(notebook.notebookDocument.cells.length, 1 + 2); // markdown and code @@ -210,7 +210,7 @@ suite('NotebookConcatDocument', function () { cellKind: CellKind.Code, outputs: [], }]]] - }); + }, false); assert.equal(notebook.notebookDocument.cells.length, 1 + 1); assert.equal(doc.version, 1); assertLines(doc, 'Hello', 'World', 'Hello World!'); @@ -233,7 +233,7 @@ suite('NotebookConcatDocument', function () { cellKind: CellKind.Code, outputs: [], }]]] - }); + }, false); assert.equal(notebook.notebookDocument.cells.length, 1 + 2); assert.equal(doc.version, 2); @@ -249,7 +249,7 @@ suite('NotebookConcatDocument', function () { kind: NotebookCellsChangeType.ModelChange, versionId: notebook.notebookDocument.version + 1, changes: [[1, 1, []]] - }); + }, false); assert.equal(notebook.notebookDocument.cells.length, 1 + 1); assert.equal(doc.version, 3); assertLines(doc, 'Hello', 'World', 'Hello World!'); @@ -283,7 +283,7 @@ suite('NotebookConcatDocument', function () { cellKind: CellKind.Code, outputs: [], }]]] - }); + }, false); assert.equal(notebook.notebookDocument.cells.length, 1 + 2); assert.equal(doc.version, 1); @@ -337,7 +337,7 @@ suite('NotebookConcatDocument', function () { cellKind: CellKind.Code, outputs: [], }]]] - }); + }, false); const mixedDoc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, undefined); const fooLangDoc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, 'fooLang'); @@ -359,7 +359,7 @@ suite('NotebookConcatDocument', function () { cellKind: CellKind.Code, outputs: [], }]]] - }); + }, false); assertLines(mixedDoc, 'fooLang-document', 'barLang-document', 'barLang-document2'); assertLines(fooLangDoc, 'fooLang-document'); @@ -401,7 +401,7 @@ suite('NotebookConcatDocument', function () { cellKind: CellKind.Code, outputs: [], }]]] - }); + }, false); assert.equal(notebook.notebookDocument.cells.length, 1 + 2); // markdown and code @@ -454,7 +454,7 @@ suite('NotebookConcatDocument', function () { cellKind: CellKind.Code, outputs: [], }]]] - }); + }, false); assert.equal(notebook.notebookDocument.cells.length, 1 + 2); // markdown and code @@ -491,7 +491,7 @@ suite('NotebookConcatDocument', function () { cellKind: CellKind.Code, outputs: [], }]]] - }); + }, false); assert.equal(notebook.notebookDocument.cells.length, 1 + 2); // markdown and code @@ -525,7 +525,7 @@ suite('NotebookConcatDocument', function () { cellKind: CellKind.Code, outputs: [], }]]] - }); + }, false); assert.equal(notebook.notebookDocument.cells.length, 1 + 2); // markdown and code From f03b1419422806727a33fd01bb2fc191ea38d096 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 28 Aug 2020 18:02:45 +0200 Subject: [PATCH 674/736] add web extensions filter --- .../contrib/extensions/browser/extensionsViewlet.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index a154ca87f76..cd57561e753 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -59,6 +59,7 @@ import { IPreferencesService } from 'vs/workbench/services/preferences/common/pr import { DragAndDropObserver } from 'vs/workbench/browser/dnd'; import { URI } from 'vs/base/common/uri'; import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; const NonEmptyWorkspaceContext = new RawContextKey('nonEmptyWorkspace', false); const DefaultViewsContext = new RawContextKey('defaultExtensionViews', true); @@ -349,6 +350,8 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE @IInstantiationService instantiationService: IInstantiationService, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, + @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, @INotificationService private readonly notificationService: INotificationService, @IViewletService private readonly viewletService: IViewletService, @@ -519,14 +522,19 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE ]); if (this.extensionGalleryService.isEnabled()) { - filterActions.splice(0, 0, ...[ + const galleryFilterActions = [ this.instantiationService.createInstance(PredefinedExtensionFilterAction, 'extensions.filter.featured', localize('featured filter', "Featured"), '@featured'), this.instantiationService.createInstance(PredefinedExtensionFilterAction, 'extensions.filter.popular', localize('most popular filter', "Most Popular"), '@popular'), this.instantiationService.createInstance(PredefinedExtensionFilterAction, 'extensions.filter.recommended', localize('most popular recommended', "Recommended"), '@recommended'), this.instantiationService.createInstance(RecentlyPublishedExtensionsAction, RecentlyPublishedExtensionsAction.ID, localize('recently published filter', "Recently Published")), + new Separator(), new SubmenuAction('workbench.extensions.action.filterExtensionsByCategory', localize('filter by category', "Category"), EXTENSION_CATEGORIES.map(category => this.instantiationService.createInstance(SearchCategoryAction, `extensions.actions.searchByCategory.${category}`, category, category))), new Separator(), - ]); + ]; + if (this.extensionManagementServerService.webExtensionManagementServer || !this.environmentService.isBuilt) { + galleryFilterActions.splice(4, 0, this.instantiationService.createInstance(PredefinedExtensionFilterAction, 'extensions.filter.web', localize('web filter', "Web"), '@web')); + } + filterActions.splice(0, 0, ...galleryFilterActions); filterActions.push(...[ new Separator(), new SubmenuAction('workbench.extensions.action.sortBy', localize('sorty by', "Sort By"), this.sortActions), @@ -580,6 +588,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE .replace(/@tag:/g, 'tag:') .replace(/@ext:/g, 'ext:') .replace(/@featured/g, 'featured') + .replace(/@web/g, 'tag:"__web_extension"') .replace(/@popular/g, '@sort:installs') : ''; } From fdf29107ad6598c0db815998b33ea4562a7e5c7b Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 28 Aug 2020 18:32:57 +0200 Subject: [PATCH 675/736] repl: simplify filter work --- .../workbench/contrib/debug/browser/repl.ts | 26 ++--- .../contrib/debug/browser/replFilter.ts | 103 ++++-------------- .../contrib/debug/common/replModel.ts | 11 +- .../contrib/debug/test/browser/repl.test.ts | 25 +++-- .../contrib/markers/browser/messages.ts | 2 +- 5 files changed, 46 insertions(+), 121 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 14d45b12304..7aa395b1c9b 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -59,7 +59,7 @@ import { ReplGroup } from 'vs/workbench/contrib/debug/common/replModel'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { EDITOR_FONT_DEFAULTS, EditorOption } from 'vs/editor/common/config/editorOptions'; import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; -import { ReplFilter, TreeFilterState, TreeFilterPanelActionViewItem } from 'vs/workbench/contrib/debug/browser/replFilter'; +import { ReplFilter, ReplFilterState, ReplFilterActionViewItem } from 'vs/workbench/contrib/debug/browser/replFilter'; const $ = dom.$; @@ -95,8 +95,8 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { private completionItemProvider: IDisposable | undefined; private modelChangeListener: IDisposable = Disposable.None; private filter: ReplFilter; - private filterState: TreeFilterState; - private filterActionViewItem: TreeFilterPanelActionViewItem | undefined; + private filterState: ReplFilterState; + private filterActionViewItem: ReplFilterActionViewItem | undefined; constructor( options: IViewPaneOptions, @@ -121,11 +121,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { this.history = new HistoryNavigator(JSON.parse(this.storageService.get(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE, '[]')), 50); this.filter = new ReplFilter(); - this.filterState = this._register(new TreeFilterState({ - filterText: '', - filterHistory: [], - layout: new dom.Dimension(0, 0), - })); + this.filterState = new ReplFilterState(); codeEditorService.registerDecorationType(DECORATION_KEY, {}); this.registerListeners(); @@ -249,13 +245,10 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { this.setMode(); })); - this._register(this.filterState.onDidChange((e) => { - if (e.filterText) { - this.filter.filterQuery = this.filterState.filterText; - if (this.tree) { - this.tree.refilter(); - } - } + this._register(this.filterState.onDidChange(() => { + this.filter.filterQuery = this.filterState.filterText; + this.tree.refilter(); + revealLastElement(this.tree); })); } @@ -448,7 +441,6 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { this.replInputContainer.style.height = `${replInputHeight}px`; this.replInput.layout({ width: width - 30, height: replInputHeight }); - this.filterState.layout = new dom.Dimension(width, height); } focus(): void { @@ -459,7 +451,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { if (action.id === SelectReplAction.ID) { return this.instantiationService.createInstance(SelectReplActionViewItem, this.selectReplAction); } else if (action.id === FILTER_ACTION_ID) { - this.filterActionViewItem = this.instantiationService.createInstance(TreeFilterPanelActionViewItem, action, localize('workbench.debug.filter.placeholder', "Filter. E.g.: text, !exclude"), this.filterState); + this.filterActionViewItem = this.instantiationService.createInstance(ReplFilterActionViewItem, action, localize('workbench.debug.filter.placeholder', "Filter (e.g. text, !exclude)"), this.filterState); return this.filterActionViewItem; } diff --git a/src/vs/workbench/contrib/debug/browser/replFilter.ts b/src/vs/workbench/contrib/debug/browser/replFilter.ts index e5675cf2bab..036e0309564 100644 --- a/src/vs/workbench/contrib/debug/browser/replFilter.ts +++ b/src/vs/workbench/contrib/debug/browser/replFilter.ts @@ -15,7 +15,7 @@ import { IAction } from 'vs/base/common/actions'; import { HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { toDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { toDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -76,64 +76,37 @@ export class ReplFilter implements ITreeFilter { } } -export interface IReplFiltersChangeEvent { - filterText?: boolean; - layout?: boolean; -} +export class ReplFilterState { -export interface IReplFiltersOptions { - filterText: string; - filterHistory: string[]; - layout: DOM.Dimension; -} - -export class TreeFilterState extends Disposable { - - private readonly _onDidChange: Emitter = this._register(new Emitter()); - readonly onDidChange: Event = this._onDidChange.event; - - constructor(options: IReplFiltersOptions) { - super(); - this._filterText = options.filterText; - this.filterHistory = options.filterHistory; - this._layout = options.layout; + private readonly _onDidChange: Emitter = new Emitter(); + get onDidChange(): Event { + return this._onDidChange.event; } - private _filterText: string; + private _filterText = ''; + get filterText(): string { return this._filterText; } + set filterText(filterText: string) { if (this._filterText !== filterText) { this._filterText = filterText; - this._onDidChange.fire({ filterText: true }); - } - } - - filterHistory: string[]; - - private _layout: DOM.Dimension = new DOM.Dimension(0, 0); - get layout(): DOM.Dimension { - return this._layout; - } - set layout(layout: DOM.Dimension) { - if (this._layout.width !== layout.width || this._layout.height !== layout.height) { - this._layout = layout; - this._onDidChange.fire({ layout: true }); + this._onDidChange.fire(); } } } -export class TreeFilterPanelActionViewItem extends BaseActionViewItem { +export class ReplFilterActionViewItem extends BaseActionViewItem { private delayedFilterUpdate: Delayer; - private container: HTMLElement | undefined; - private filterInputBox: HistoryInputBox | undefined; + private container!: HTMLElement; + private filterInputBox!: HistoryInputBox; constructor( action: IAction, private placeholder: string, - private filters: TreeFilterState, + private filters: ReplFilterState, @IInstantiationService private readonly instantiationService: IInstantiationService, @IThemeService private readonly themeService: IThemeService, @IContextViewService private readonly contextViewService: IContextViewService) { @@ -150,34 +123,28 @@ export class TreeFilterPanelActionViewItem extends BaseActionViewItem { this.element.className = this.class; this.createInput(this.element); this.updateClass(); - - this.adjustInputBox(); + this.filterInputBox.inputElement.style.paddingRight = DOM.hasClass(this.element, 'small') ? '25px' : '150px'; } focus(): void { - if (this.filterInputBox) { - this.filterInputBox.focus(); - } + this.filterInputBox.focus(); } private clearFilterText(): void { - if (this.filterInputBox) { - this.filterInputBox.value = ''; - } + this.filterInputBox.value = ''; } private createInput(container: HTMLElement): void { this.filterInputBox = this._register(this.instantiationService.createInstance(ContextScopedHistoryInputBox, container, this.contextViewService, { placeholder: this.placeholder, - history: this.filters.filterHistory + history: [] })); this._register(attachInputBoxStyler(this.filterInputBox, this.themeService)); this.filterInputBox.value = this.filters.filterText; + this._register(this.filterInputBox.onDidChange(() => this.delayedFilterUpdate.trigger(() => this.onDidInputChange(this.filterInputBox!)))); - this._register(this.filters.onDidChange((event: IReplFiltersChangeEvent) => { - if (event.filterText) { - this.filterInputBox!.value = this.filters.filterText; - } + this._register(this.filters.onDidChange(() => { + this.filterInputBox.value = this.filters.filterText; })); this._register(DOM.addStandardDisposableListener(this.filterInputBox.inputElement, DOM.EventType.KEY_DOWN, (e: any) => this.onInputKeyDown(e))); this._register(DOM.addStandardDisposableListener(container, DOM.EventType.KEY_DOWN, this.handleKeyboardEvent)); @@ -186,19 +153,11 @@ export class TreeFilterPanelActionViewItem extends BaseActionViewItem { e.stopPropagation(); e.preventDefault(); })); - this._register(this.filters.onDidChange(e => this.onDidFiltersChange(e))); - } - - private onDidFiltersChange(e: IReplFiltersChangeEvent): void { - if (e.layout) { - this.updateClass(); - } } private onDidInputChange(inputbox: HistoryInputBox) { inputbox.addToHistory(); this.filters.filterText = inputbox.value; - this.filters.filterHistory = inputbox.getHistory(); } // Action toolbar is swallowing some keys for action items which should not be for an input box @@ -220,27 +179,7 @@ export class TreeFilterPanelActionViewItem extends BaseActionViewItem { } } - private adjustInputBox(): void { - if (this.element && this.filterInputBox) { - this.filterInputBox.inputElement.style.paddingRight = DOM.hasClass(this.element, 'small') ? '25px' : '150px'; - } - } - - protected updateClass(): void { - if (this.element && this.container) { - this.element.className = this.class; - DOM.toggleClass(this.container, 'grow', DOM.hasClass(this.element, 'grow')); - this.adjustInputBox(); - } - } - protected get class(): string { - if (this.filters.layout.width > 800) { - return 'panel-action-tree-filter grow'; - } else if (this.filters.layout.width < 600) { - return 'panel-action-tree-filter small'; - } else { - return 'panel-action-tree-filter'; - } + return 'panel-action-tree-filter'; } } diff --git a/src/vs/workbench/contrib/debug/common/replModel.ts b/src/vs/workbench/contrib/debug/common/replModel.ts index b0aa0128449..87d3348b97c 100644 --- a/src/vs/workbench/contrib/debug/common/replModel.ts +++ b/src/vs/workbench/contrib/debug/common/replModel.ts @@ -174,18 +174,13 @@ export class ReplGroup implements IReplElement { } } -type FilterFunc = ((element: IReplElement) => void); - export class ReplModel { private replElements: IReplElement[] = []; private readonly _onDidChangeElements = new Emitter(); readonly onDidChangeElements = this._onDidChangeElements.event; - private filterFunc: FilterFunc | undefined; getReplElements(): IReplElement[] { - return this.replElements.filter(element => - this.filterFunc ? this.filterFunc(element) : true - ); + return this.replElements; } async addReplExpression(session: IDebugSession, stackFrame: IStackFrame | undefined, name: string): Promise { @@ -320,10 +315,6 @@ export class ReplModel { } } - setFilter(filterFunc: FilterFunc): void { - this.filterFunc = filterFunc; - } - removeReplExpressions(): void { if (this.replElements.length > 0) { this.replElements = []; diff --git a/src/vs/workbench/contrib/debug/test/browser/repl.test.ts b/src/vs/workbench/contrib/debug/test/browser/repl.test.ts index 7440c4df8b8..787328d4e5d 100644 --- a/src/vs/workbench/contrib/debug/test/browser/repl.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/repl.test.ts @@ -197,10 +197,13 @@ suite('Debug - REPL', () => { const repl = new ReplModel(); const replFilter = new ReplFilter(); - repl.setFilter((element) => { - const filterResult = replFilter.filter(element, TreeVisibility.Visible); - return filterResult === true || filterResult === TreeVisibility.Visible; - }); + const getFilteredElements = () => { + const elements = repl.getReplElements(); + return elements.filter(e => { + const filterResult = replFilter.filter(e, TreeVisibility.Visible); + return filterResult === true || filterResult === TreeVisibility.Visible; + }); + }; repl.appendToRepl(session, 'first line\n', severity.Info); repl.appendToRepl(session, 'second line\n', severity.Info); @@ -208,19 +211,19 @@ suite('Debug - REPL', () => { repl.appendToRepl(session, 'fourth line\n', severity.Info); replFilter.filterQuery = 'first'; - let r1 = repl.getReplElements(); + let r1 = getFilteredElements(); assert.equal(r1.length, 1); assert.equal(r1[0].value, 'first line\n'); replFilter.filterQuery = '!first'; - let r2 = repl.getReplElements(); + let r2 = getFilteredElements(); assert.equal(r1.length, 1); assert.equal(r2[0].value, 'second line\n'); assert.equal(r2[1].value, 'third line\n'); assert.equal(r2[2].value, 'fourth line\n'); replFilter.filterQuery = 'first, line'; - let r3 = repl.getReplElements(); + let r3 = getFilteredElements(); assert.equal(r3.length, 4); assert.equal(r3[0].value, 'first line\n'); assert.equal(r3[1].value, 'second line\n'); @@ -228,22 +231,22 @@ suite('Debug - REPL', () => { assert.equal(r3[3].value, 'fourth line\n'); replFilter.filterQuery = 'line, !second'; - let r4 = repl.getReplElements(); + let r4 = getFilteredElements(); assert.equal(r4.length, 3); assert.equal(r4[0].value, 'first line\n'); assert.equal(r4[1].value, 'third line\n'); assert.equal(r4[2].value, 'fourth line\n'); replFilter.filterQuery = '!second, line'; - let r4_same = repl.getReplElements(); + let r4_same = getFilteredElements(); assert.equal(r4.length, r4_same.length); replFilter.filterQuery = '!line'; - let r5 = repl.getReplElements(); + let r5 = getFilteredElements(); assert.equal(r5.length, 0); replFilter.filterQuery = 'smth'; - let r6 = repl.getReplElements(); + let r6 = getFilteredElements(); assert.equal(r6.length, 0); }); }); diff --git a/src/vs/workbench/contrib/markers/browser/messages.ts b/src/vs/workbench/contrib/markers/browser/messages.ts index 4fbffb9d7d4..4652f48e7f1 100644 --- a/src/vs/workbench/contrib/markers/browser/messages.ts +++ b/src/vs/workbench/contrib/markers/browser/messages.ts @@ -33,7 +33,7 @@ export default class Messages { public static MARKERS_PANEL_ACTION_TOOLTIP_FILTER: string = nls.localize('markers.panel.action.filter', "Filter Problems"); public static MARKERS_PANEL_ACTION_TOOLTIP_QUICKFIX: string = nls.localize('markers.panel.action.quickfix', "Show fixes"); public static MARKERS_PANEL_FILTER_ARIA_LABEL: string = nls.localize('markers.panel.filter.ariaLabel', "Filter Problems"); - public static MARKERS_PANEL_FILTER_PLACEHOLDER: string = nls.localize('markers.panel.filter.placeholder', "Filter. E.g.: text, **/*.ts, !**/node_modules/**"); + public static MARKERS_PANEL_FILTER_PLACEHOLDER: string = nls.localize('markers.panel.filter.placeholder', "Filter (e.g. text, **/*.ts, !**/node_modules/**)"); public static MARKERS_PANEL_FILTER_ERRORS: string = nls.localize('markers.panel.filter.errors', "errors"); public static MARKERS_PANEL_FILTER_WARNINGS: string = nls.localize('markers.panel.filter.warnings', "warnings"); public static MARKERS_PANEL_FILTER_INFOS: string = nls.localize('markers.panel.filter.infos', "infos"); From 637d782c7b3c698a1bb4e41d750c9db77b6012b5 Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 28 Aug 2020 12:04:03 -0700 Subject: [PATCH 676/736] re #102503. visible range change events. --- src/vs/vscode.proposed.d.ts | 17 +++ .../api/browser/mainThreadNotebook.ts | 73 ++++++++---- .../workbench/api/common/extHost.api.impl.ts | 4 + .../workbench/api/common/extHost.protocol.ts | 23 +++- .../workbench/api/common/extHostNotebook.ts | 54 ++++++++- .../notebook/browser/contrib/fold/folding.ts | 4 +- .../browser/contrib/fold/foldingModel.ts | 3 +- .../notebook/browser/notebookBrowser.ts | 30 ++--- .../notebook/browser/notebookEditorWidget.ts | 15 ++- .../notebook/browser/view/notebookCellList.ts | 110 +++++++++++++++++- .../browser/view/renderers/cellRenderer.ts | 6 +- .../browser/viewModel/notebookViewModel.ts | 4 +- .../contrib/notebook/common/notebookCommon.ts | 17 +++ .../notebook/test/notebookViewModel.test.ts | 4 +- .../notebook/test/testNotebookEditor.ts | 8 +- .../test/browser/api/extHostNotebook.test.ts | 3 +- .../api/extHostNotebookConcatDocument.test.ts | 3 +- 17 files changed, 315 insertions(+), 63 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 32bf845aa31..370cb790ec6 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1363,6 +1363,11 @@ declare module 'vscode' { delete(index: number): void; } + export interface NotebookCellRange { + readonly start: number; + readonly end: number; + } + export interface NotebookEditor { /** * The document associated with this notebook editor. @@ -1374,6 +1379,12 @@ declare module 'vscode' { */ readonly selection?: NotebookCell; + + /** + * The current visible ranges in the editor (vertically). + */ + readonly visibleRanges: NotebookCellRange[]; + /** * The column in which this editor shows. */ @@ -1485,6 +1496,11 @@ declare module 'vscode' { readonly selection?: NotebookCell; } + export interface NotebookEditorVisibleRangesChangeEvent { + readonly notebookEditor: NotebookEditor; + readonly visibleRanges: ReadonlyArray; + } + export interface NotebookCellData { readonly cellKind: CellKind; readonly source: string; @@ -1707,6 +1723,7 @@ declare module 'vscode' { export const activeNotebookEditor: NotebookEditor | undefined; export const onDidChangeActiveNotebookEditor: Event; export const onDidChangeNotebookEditorSelection: Event; + export const onDidChangeNotebookEditorVisibleRanges: Event; export const onDidChangeNotebookCells: Event; export const onDidChangeCellOutputs: Event; export const onDidChangeCellLanguage: Event; diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index dd8b3f29585..4cca5edccdb 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -60,7 +60,7 @@ class DocumentAndEditorState { const apiEditors = []; for (let id in after.textEditors) { const editor = after.textEditors.get(id)!; - apiEditors.push({ id, documentUri: editor.uri!, selections: editor!.textModel!.selections }); + apiEditors.push({ id, documentUri: editor.uri!, selections: editor!.textModel!.selections, visibleRanges: editor.visibleRanges }); } return { @@ -74,7 +74,8 @@ class DocumentAndEditorState { const addedAPIEditors = editorDelta.added.map(add => ({ id: add.getId(), documentUri: add.uri!, - selections: add.textModel!.selections || [] + selections: add.textModel!.selections || [], + visibleRanges: add.visibleRanges })); const removedAPIEditors = editorDelta.removed.map(removed => removed.getId()); @@ -137,6 +138,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo private _toDisposeOnEditorRemove = new Map(); private _currentState?: DocumentAndEditorState; private _editorEventListenersMapping: Map = new Map(); + private _documentEventListenersMapping: Map = new Map(); private readonly _cellStatusBarEntries: Map = new Map(); constructor( @@ -166,9 +168,9 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo async removeNotebookTextModel(uri: URI): Promise { // TODO@rebornix, remove cell should use emitDelta as well to ensure document/editor events are sent together this._proxy.$acceptDocumentAndEditorsDelta({ removedDocuments: [uri] }); - let textModelDisposableStore = this._editorEventListenersMapping.get(uri.toString()); + let textModelDisposableStore = this._documentEventListenersMapping.get(uri.toString()); textModelDisposableStore?.dispose(); - this._editorEventListenersMapping.delete(URI.from(uri).toString()); + this._documentEventListenersMapping.delete(URI.from(uri).toString()); } private _isDeltaEmpty(delta: INotebookDocumentsAndEditorsDelta) { @@ -228,38 +230,67 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo } })); + const notebookEditorAddedHandler = (editor: IEditor) => { + if (!this._editorEventListenersMapping.has(editor.getId())) { + const disposableStore = new DisposableStore(); + disposableStore.add(editor.onDidChangeVisibleRanges(() => { + this._proxy.$acceptEditorPropertiesChanged(editor.getId(), { visibleRanges: { ranges: editor.visibleRanges } }); + })); + + this._editorEventListenersMapping.set(editor.getId(), disposableStore); + } + }; + this._register(this._notebookService.onNotebookEditorAdd(editor => { + notebookEditorAddedHandler(editor); this._addNotebookEditor(editor); })); this._register(this._notebookService.onNotebookEditorsRemove(editors => { this._removeNotebookEditor(editors); + + editors.forEach(editor => { + this._editorEventListenersMapping.get(editor.getId())?.dispose(); + this._editorEventListenersMapping.delete(editor.getId()); + }); })); + this._notebookService.listNotebookEditors().forEach(editor => { + notebookEditorAddedHandler(editor); + }); + + const notebookDocumentAddedHandler = (doc: URI) => { + if (!this._editorEventListenersMapping.has(doc.toString())) { + const disposableStore = new DisposableStore(); + const textModel = this._notebookService.getNotebookTextModel(doc); + disposableStore.add(textModel!.onDidModelChangeProxy(e => { + this._proxy.$acceptModelChanged(textModel!.uri, e, textModel!.isDirty); + this._proxy.$acceptDocumentPropertiesChanged(doc, { selections: { selections: textModel!.selections }, metadata: null }); + })); + disposableStore.add(textModel!.onDidSelectionChange(e => { + const selectionsChange = e ? { selections: e } : null; + this._proxy.$acceptDocumentPropertiesChanged(doc, { selections: selectionsChange, metadata: null }); + })); + + this._editorEventListenersMapping.set(textModel!.uri.toString(), disposableStore); + } + }; + this._register(this._notebookService.onNotebookDocumentAdd((documents) => { documents.forEach(doc => { - if (!this._editorEventListenersMapping.has(doc.toString())) { - const disposableStore = new DisposableStore(); - const textModel = this._notebookService.getNotebookTextModel(doc); - disposableStore.add(textModel!.onDidModelChangeProxy(e => { - this._proxy.$acceptModelChanged(textModel!.uri, e, textModel!.isDirty); - this._proxy.$acceptEditorPropertiesChanged(doc, { selections: { selections: textModel!.selections }, metadata: null }); - })); - disposableStore.add(textModel!.onDidSelectionChange(e => { - const selectionsChange = e ? { selections: e } : null; - this._proxy.$acceptEditorPropertiesChanged(doc, { selections: selectionsChange, metadata: null }); - })); - - this._editorEventListenersMapping.set(textModel!.uri.toString(), disposableStore); - } + notebookDocumentAddedHandler(doc); }); this._updateState(); })); + this._notebookService.listNotebookDocuments().forEach((doc) => { + notebookDocumentAddedHandler(doc.uri); + }); + this._register(this._notebookService.onNotebookDocumentRemove((documents) => { documents.forEach(doc => { - this._editorEventListenersMapping.get(doc.toString())?.dispose(); - this._editorEventListenersMapping.delete(doc.toString()); + this._documentEventListenersMapping.get(doc.toString())?.dispose(); + this._documentEventListenersMapping.delete(doc.toString()); }); this._updateState(); @@ -420,7 +451,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo textModel.insertTemplateCell(mainCell); } - this._proxy.$acceptEditorPropertiesChanged(textModel.uri, { selections: null, metadata: textModel.metadata }); + this._proxy.$acceptDocumentPropertiesChanged(textModel.uri, { selections: null, metadata: textModel.metadata }); return; }, resolveNotebookEditor: async (viewType: string, uri: URI, editorId: string) => { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index d9943430885..ab29d86e094 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -981,6 +981,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension); return extHostNotebook.onDidChangeNotebookEditorSelection(listener, thisArgs, disposables); }, + onDidChangeNotebookEditorVisibleRanges(listener, thisArgs?, disposables?) { + checkProposedApiEnabled(extension); + return extHostNotebook.onDidChangeNotebookEditorVisibleRanges(listener, thisArgs, disposables); + }, onDidChangeCellOutputs(listener, thisArgs?, disposables?) { checkProposedApiEnabled(extension); return extHostNotebook.onDidChangeCellOutputs(listener, thisArgs, disposables); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 25182aee42f..d4bdfcfa5c2 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -51,7 +51,7 @@ import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; import { TunnelOptions } from 'vs/platform/remote/common/tunnel'; import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline'; import { revive } from 'vs/base/common/marshalling'; -import { IProcessedOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEvent, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, INotebookKernelInfoDto2, TransientMetadata, INotebookCellStatusBarEntry } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { IProcessedOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEvent, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, INotebookKernelInfoDto2, TransientMetadata, INotebookCellStatusBarEntry, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; import { Dto } from 'vs/base/common/types'; import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; @@ -1624,9 +1624,22 @@ export interface INotebookSelectionChangeEvent { selections: number[]; } +export interface INotebookCellVisibleRange { + start: number; + end: number; +} + +export interface INotebookVisibleRangesEvent { + ranges: INotebookCellVisibleRange[]; +} + export interface INotebookEditorPropertiesChangeData { - selections: INotebookSelectionChangeEvent | null; + visibleRanges: INotebookVisibleRangesEvent | null; +} + +export interface INotebookDocumentPropertiesChangeData { metadata: NotebookDocumentMetadata | null; + selections: INotebookSelectionChangeEvent | null; } export interface INotebookModelAddedData { @@ -1636,13 +1649,14 @@ export interface INotebookModelAddedData { cells: IMainCellDto[], viewType: string; metadata?: NotebookDocumentMetadata; - attachedEditor?: { id: string; selections: number[]; } + attachedEditor?: { id: string; selections: number[]; visibleRanges: ICellRange[] } } export interface INotebookEditorAddData { id: string; documentUri: UriComponents; selections: number[]; + visibleRanges: ICellRange[]; } export interface INotebookDocumentsAndEditorsDelta { @@ -1670,7 +1684,8 @@ export interface ExtHostNotebookShape { $onDidReceiveMessage(editorId: string, rendererId: string | undefined, message: unknown): void; $acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent, isDirty: boolean): void; $acceptModelSaved(uriComponents: UriComponents): void; - $acceptEditorPropertiesChanged(uriComponents: UriComponents, data: INotebookEditorPropertiesChangeData): void; + $acceptEditorPropertiesChanged(id: string, data: INotebookEditorPropertiesChangeData): void; + $acceptDocumentPropertiesChanged(uriComponents: UriComponents, data: INotebookDocumentPropertiesChangeData): void; $acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta): void; $undoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise; $redoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 379d6c285bd..8aeeb8e0b98 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -14,7 +14,7 @@ import { ISplice } from 'vs/base/common/sequence'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as UUID from 'vs/base/common/uuid'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { CellKind, ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentsAndEditorsDelta, INotebookEditorPropertiesChangeData, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice } from 'vs/workbench/api/common/extHost.protocol'; +import { CellKind, ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookEditorPropertiesChangeData, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice } from 'vs/workbench/api/common/extHost.protocol'; import { ILogService } from 'vs/platform/log/common/log'; import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; @@ -633,6 +633,20 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook selection?: vscode.NotebookCell; + private _visibleRanges: vscode.NotebookCellRange[] = []; + + get visibleRanges() { + return this._visibleRanges; + } + + set visibleRanges(_range: vscode.NotebookCellRange[]) { + throw readonly('visibleRanges'); + } + + _acceptVisibleRanges(value: vscode.NotebookCellRange[]): void { + this._visibleRanges = value; + } + private _active: boolean = false; get active(): boolean { return this._active; @@ -886,6 +900,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN private readonly _commandsConverter: CommandsConverter; private readonly _onDidChangeNotebookEditorSelection = new Emitter(); readonly onDidChangeNotebookEditorSelection = this._onDidChangeNotebookEditorSelection.event; + private readonly _onDidChangeNotebookEditorVisibleRanges = new Emitter(); + readonly onDidChangeNotebookEditorVisibleRanges = this._onDidChangeNotebookEditorVisibleRanges.event; private readonly _onDidChangeNotebookCells = new Emitter(); readonly onDidChangeNotebookCells = this._onDidChangeNotebookCells.event; private readonly _onDidChangeCellOutputs = new Emitter(); @@ -1284,8 +1300,31 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } } - $acceptEditorPropertiesChanged(uriComponents: UriComponents, data: INotebookEditorPropertiesChangeData): void { - this.logService.debug('ExtHostNotebook#$acceptEditorPropertiesChanged', uriComponents.path, data); + $acceptEditorPropertiesChanged(id: string, data: INotebookEditorPropertiesChangeData): void { + this.logService.debug('ExtHostNotebook#$acceptEditorPropertiesChanged', id, data); + + let editor: { editor: ExtHostNotebookEditor; } | undefined; + this._editors.forEach(e => { + if (e.editor.id === id) { + editor = e; + } + }); + + if (!editor) { + return; + } + + if (data.visibleRanges) { + editor.editor._acceptVisibleRanges(data.visibleRanges.ranges); + this._onDidChangeNotebookEditorVisibleRanges.fire({ + notebookEditor: editor.editor, + visibleRanges: editor.editor.visibleRanges + }); + } + } + + $acceptDocumentPropertiesChanged(uriComponents: UriComponents, data: INotebookDocumentPropertiesChangeData): void { + this.logService.debug('ExtHostNotebook#$acceptDocumentPropertiesChanged', uriComponents.path, data); const editor = this._getEditorFromURI(uriComponents); if (!editor) { @@ -1306,6 +1345,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN }); } + if (data.metadata) { editor.editor.notebookData.notebookDocument.metadata = { ...notebookDocumentMetadataDefaults, @@ -1314,7 +1354,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } } - private _createExtHostEditor(document: ExtHostNotebookDocument, editorId: string, selections: number[]) { + private _createExtHostEditor(document: ExtHostNotebookDocument, editorId: string, selections: number[], visibleRanges: vscode.NotebookCellRange[]) { const revivedUri = document.uri; let webComm = this._webviewComm.get(editorId); @@ -1339,6 +1379,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN editor.selection = undefined; } + editor._acceptVisibleRanges(visibleRanges); + this._editors.get(editorId)?.editor.dispose(); this._editors.set(editorId, { editor }); } @@ -1423,7 +1465,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN // create editor if populated if (modelData.attachedEditor) { - this._createExtHostEditor(document, modelData.attachedEditor.id, modelData.attachedEditor.selections); + this._createExtHostEditor(document, modelData.attachedEditor.id, modelData.attachedEditor.selections, modelData.attachedEditor.visibleRanges); editorChanged = true; } } @@ -1445,7 +1487,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN const document = this._documents.get(revivedUri); if (document) { - this._createExtHostEditor(document, editorModelData.id, editorModelData.selections); + this._createExtHostEditor(document, editorModelData.id, editorModelData.selections, editorModelData.visibleRanges); editorChanged = true; } } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts b/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts index 6b24b18e9a2..aa7711d6817 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { INotebookEditor, INotebookEditorMouseEvent, ICellRange, INotebookEditorContribution, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { INotebookEditor, INotebookEditorMouseEvent, INotebookEditorContribution, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import * as DOM from 'vs/base/browser/dom'; import { CellFoldingState, FoldingModel } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel'; -import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; import { registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts b/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts index fade3589aa0..3675729c2dd 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts @@ -8,9 +8,8 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { TrackedRangeStickiness } from 'vs/editor/common/model'; import { FoldingRegion, FoldingRegions } from 'vs/editor/contrib/folding/foldingRanges'; import { IFoldingRangeData, sanitizeRanges } from 'vs/editor/contrib/folding/syntaxRangeProvider'; -import { ICellRange } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookCommon'; type RegionFilter = (r: FoldingRegion) => boolean; type RegionFilterWithLevel = (r: FoldingRegion, level: number) => boolean; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index a3b61443510..05194d40170 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -22,7 +22,7 @@ import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/outpu import { RunStateRenderer, TimerRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer'; import { CellViewModel, IModelDecorationsChangeAccessor, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { CellKind, IProcessedOutput, IRenderOutput, NotebookCellMetadata, NotebookDocumentMetadata, INotebookKernelInfo, IEditor, INotebookKernelInfo2, IInsetRenderOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, IProcessedOutput, IRenderOutput, NotebookCellMetadata, NotebookDocumentMetadata, INotebookKernelInfo, IEditor, INotebookKernelInfo2, IInsetRenderOutput, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { IMenu } from 'vs/platform/actions/common/actions'; @@ -465,6 +465,8 @@ export interface INotebookCellList { onDidScroll: Event; onDidChangeFocus: Event>; onDidChangeContentHeight: Event; + onDidChangeVisibleRanges: Event; + visibleRanges: ICellRange[]; scrollTop: number; scrollHeight: number; scrollLeft: number; @@ -517,6 +519,7 @@ export interface BaseCellRenderTemplate { contextKeyService: IContextKeyService; container: HTMLElement; cellContainer: HTMLElement; + decorationContainer: HTMLElement; toolbar: ToolBar; deleteToolbar: ToolBar; betweenCellToolbar: ToolBar; @@ -622,19 +625,20 @@ export interface CellViewModelStateChangeEvent { outputIsHoveredChanged?: boolean; } -/** - * [start, end] - */ -export interface ICellRange { - /** - * zero based index - */ - start: number; +export function cellRangesEqual(a: ICellRange[], b: ICellRange[]) { + a = reduceCellRanges(a); + b = reduceCellRanges(b); + if (a.length !== b.length) { + return false; + } - /** - * zero based index - */ - end: number; + for (let i = 0; i < a.length; i++) { + if (a[i].start !== b[i].start || a[i].end !== b[i].end) { + return false; + } + } + + return true; } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index f12b346c3e6..e65eca7edf8 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -38,7 +38,7 @@ import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { PANEL_BORDER } from 'vs/workbench/common/theme'; import { debugIconStartForeground } from 'vs/workbench/contrib/debug/browser/debugToolBar'; import { BOTTOM_CELL_TOOLBAR_GAP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_MARGIN, CELL_RUN_GUTTER, CELL_TOP_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, SCROLLABLE_ELEMENT_PADDING_TOP } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellEditState, CellFocusMode, ICellRange, ICellViewModel, INotebookCellList, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorMouseEvent, NotebookEditorOptions, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellEditState, CellFocusMode, ICellViewModel, INotebookCellList, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorMouseEvent, NotebookEditorOptions, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; import { NotebookKernelProviderAssociations, notebookKernelProviderAssociationsSettingId } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation'; import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList'; @@ -51,7 +51,7 @@ import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewMod import { NotebookEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { CellViewModel, IModelDecorationsChangeAccessor, INotebookEditorViewState, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellKind, CellToolbarLocKey, IInsetRenderOutput, INotebookKernelInfo, INotebookKernelInfo2, INotebookKernelInfoDto, IProcessedOutput, NotebookCellRunState, NotebookRunState, ShowCellStatusbarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellToolbarLocKey, ICellRange, IInsetRenderOutput, INotebookKernelInfo, INotebookKernelInfo2, INotebookKernelInfoDto, IProcessedOutput, NotebookCellRunState, NotebookRunState, ShowCellStatusbarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { editorGutterModifiedBackground } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; @@ -211,6 +211,13 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._cursorNavigationMode = v; } + private readonly _onDidChangeVisibleRanges = this._register(new Emitter()); + onDidChangeVisibleRanges: Event = this._onDidChangeVisibleRanges.event; + + get visibleRanges() { + return this._list?.visibleRanges || []; + } + readonly isEmbedded: boolean; constructor( @@ -521,6 +528,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._onDidScroll.fire(e); })); + this._register(this._list.onDidChangeVisibleRanges(() => { + this._onDidChangeVisibleRanges.fire(); + })); + const widgetFocusTracker = DOM.trackFocus(this.getDomNode()); this._register(widgetFocusTracker); this._register(widgetFocusTracker.onDidFocus(() => this._onDidFocusEmitter.fire())); diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index 76c404fe169..01984839042 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -19,9 +19,9 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IListService, IWorkbenchListOptions, WorkbenchList } from 'vs/platform/list/browser/listService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { CellRevealPosition, CellRevealType, CursorAtBoundary, getVisibleCells, ICellRange, ICellViewModel, INotebookCellList, reduceCellRanges, CellEditState, CellFocusMode, BaseCellRenderTemplate, NOTEBOOK_CELL_LIST_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellRevealPosition, CellRevealType, CursorAtBoundary, getVisibleCells, ICellViewModel, INotebookCellList, reduceCellRanges, CellEditState, CellFocusMode, BaseCellRenderTemplate, NOTEBOOK_CELL_LIST_FOCUSED, cellRangesEqual } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { diff, IProcessedOutput, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { diff, IProcessedOutput, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, CellKind, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { clamp } from 'vs/base/common/numbers'; import { SCROLLABLE_ELEMENT_PADDING_TOP } from 'vs/workbench/contrib/notebook/browser/constants'; @@ -54,12 +54,32 @@ export class NotebookCellList extends WorkbenchList implements ID private _hiddenRangeIds: string[] = []; private hiddenRangesPrefixSum: PrefixSumComputer | null = null; + private readonly _onDidChangeVisibleRanges = new Emitter(); + + onDidChangeVisibleRanges: Event = this._onDidChangeVisibleRanges.event; + private _visibleRanges: ICellRange[] = []; + + get visibleRanges() { + return this._visibleRanges; + } + + set visibleRanges(ranges: ICellRange[]) { + if (cellRangesEqual(this._visibleRanges, ranges)) { + return; + } + + this._visibleRanges = ranges; + this._onDidChangeVisibleRanges.fire(); + } + private _isDisposed = false; get isDisposed() { return this._isDisposed; } + private _isInLayout: boolean = false; + private readonly _focusNextPreviousDelegate: IFocusNextPreviousDelegate; constructor( @@ -152,6 +172,86 @@ export class NotebookCellList extends WorkbenchList implements ID focus.focusMode = CellFocusMode.Editor; } })); + + // update visibleRanges + const updateVisibleRanges = () => { + if (!this.view.length) { + return; + } + + const top = this.getViewScrollTop(); + const bottom = this.getViewScrollBottom(); + const topViewIndex = clamp(this.view.indexAt(top), 0, this.view.length - 1); + const topElement = this.view.element(topViewIndex); + const topModelIndex = this._viewModel!.getCellIndex(topElement); + const bottomViewIndex = clamp(this.view.indexAt(bottom), 0, this.view.length - 1); + const bottomElement = this.view.element(bottomViewIndex); + const bottomModelIndex = this._viewModel!.getCellIndex(bottomElement); + + if (bottomModelIndex - topModelIndex === bottomViewIndex - topViewIndex) { + this.visibleRanges = [{ start: topModelIndex, end: bottomModelIndex }]; + } else { + let stack: number[] = []; + const ranges: ICellRange[] = []; + // there are hidden ranges + let index = topViewIndex; + let modelIndex = topModelIndex; + + while (index <= bottomViewIndex) { + const accu = this.hiddenRangesPrefixSum!.getAccumulatedValue(index); + if (accu === modelIndex + 1) { + // no hidden area after it + if (stack.length) { + if (stack[stack.length - 1] === modelIndex - 1) { + ranges.push({ start: stack[stack.length - 1], end: modelIndex }); + } else { + ranges.push({ start: stack[stack.length - 1], end: stack[stack.length - 1] }); + } + } + + stack.push(modelIndex); + index++; + modelIndex++; + } else { + // there are hidden ranges after it + if (stack.length) { + if (stack[stack.length - 1] === modelIndex - 1) { + ranges.push({ start: stack[stack.length - 1], end: modelIndex }); + } else { + ranges.push({ start: stack[stack.length - 1], end: stack[stack.length - 1] }); + } + } + + stack.push(modelIndex); + index++; + modelIndex = accu; + } + } + + if (stack.length) { + ranges.push({ start: stack[stack.length - 1], end: stack[stack.length - 1] }); + } + + this.visibleRanges = reduceCellRanges(ranges); + } + }; + + this._localDisposableStore.add(this.view.onDidChangeContentHeight(() => { + if (this._isInLayout) { + DOM.scheduleAtNextAnimationFrame(() => { + updateVisibleRanges(); + }); + } + updateVisibleRanges(); + })); + this._localDisposableStore.add(this.view.onDidScroll(() => { + if (this._isInLayout) { + DOM.scheduleAtNextAnimationFrame(() => { + updateVisibleRanges(); + }); + } + updateVisibleRanges(); + })); } elementAt(position: number): ICellViewModel | undefined { @@ -933,6 +1033,12 @@ export class NotebookCellList extends WorkbenchList implements ID } } + layout(height?: number, width?: number): void { + this._isInLayout = true; + super.layout(height, width); + this._isInLayout = false; + } + dispose() { this._isDisposed = true; this._viewModelStore.dispose(); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index d5aeb2571af..9be48cf297a 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -383,7 +383,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR const container = DOM.append(rootContainer, DOM.$('.cell-inner-container')); const disposables = new DisposableStore(); const contextKeyService = disposables.add(this.contextKeyServiceProvider(container)); - + const decorationContainer = DOM.append(container, $('.cell-decoration')); const titleToolbarContainer = DOM.append(container, $('.cell-title-toolbar')); const toolbar = disposables.add(this.createToolbar(titleToolbarContainer)); const deleteToolbar = disposables.add(this.createToolbar(titleToolbarContainer, 'cell-delete-toolbar')); @@ -412,6 +412,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR expandButton, contextKeyService, container, + decorationContainer, cellContainer: innerContent, editorPart, editorContainer, @@ -631,7 +632,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende const container = DOM.append(rootContainer, DOM.$('.cell-inner-container')); const disposables = new DisposableStore(); const contextKeyService = disposables.add(this.contextKeyServiceProvider(container)); - + const decorationContainer = DOM.append(container, $('.cell-decoration')); DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-top')); const titleToolbarContainer = DOM.append(container, $('.cell-title-toolbar')); const toolbar = disposables.add(this.createToolbar(titleToolbarContainer)); @@ -693,6 +694,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende expandButton, contextKeyService, container, + decorationContainer, cellContainer, cellRunState, progressBar, diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts index 0c02d11cf09..64db881ca8e 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts @@ -17,13 +17,13 @@ import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { WorkspaceTextEdit } from 'vs/editor/common/modes'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; -import { CellEditState, CellFindMatch, ICellRange, ICellViewModel, NotebookLayoutInfo, IEditableCellViewModel, INotebookDeltaDecoration } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellEditState, CellFindMatch, ICellViewModel, NotebookLayoutInfo, IEditableCellViewModel, INotebookDeltaDecoration } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookEventDispatcher, NotebookMetadataChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { CellFoldingState, EditorFoldingStateDelegate } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel'; import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { CellKind, NotebookCellMetadata, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, NotebookCellMetadata, INotebookSearchOptions, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { FoldingRegions } from 'vs/editor/contrib/folding/foldingRanges'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { MarkdownRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/mdRenderer'; diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 8300e9f3950..c6be8ff48ab 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -685,10 +685,27 @@ export interface NotebookDocumentBackupData { readonly mtime?: number; } +/** + * [start, end] + */ +export interface ICellRange { + /** + * zero based index + */ + start: number; + + /** + * zero based index + */ + end: number; +} + export interface IEditor extends editorCommon.ICompositeCodeEditor { readonly onDidChangeModel: Event; readonly onDidFocusEditorWidget: Event; + readonly onDidChangeVisibleRanges: Event; isNotebookEditor: boolean; + visibleRanges: ICellRange[]; uri?: URI; textModel?: NotebookTextModel; getId(): string; diff --git a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts index 70d9b950c3f..b45c690f1af 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts @@ -6,14 +6,14 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { CellKind, NotebookCellMetadata, diff } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, NotebookCellMetadata, diff, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { withTestNotebook, TestCell, NotebookEditorTestModel, setupInstantiationService } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { TrackedRangeStickiness } from 'vs/editor/common/model'; -import { reduceCellRanges, ICellRange } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { reduceCellRanges } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; suite('NotebookViewModel', () => { diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index 7953687f0ce..3bb3c92a63d 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -12,13 +12,13 @@ import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { Range } from 'vs/editor/common/core/range'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { EditorModel } from 'vs/workbench/common/editor'; -import { ICellRange, ICellViewModel, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo, INotebookDeltaDecoration, INotebookEditorCreationOptions, NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { ICellViewModel, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo, INotebookDeltaDecoration, INotebookEditorCreationOptions, NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer'; import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { CellViewModel, IModelDecorationsChangeAccessor, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellKind, CellUri, INotebookEditorModel, IProcessedOutput, NotebookCellMetadata, INotebookKernelInfo, IInsetRenderOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellUri, INotebookEditorModel, IProcessedOutput, NotebookCellMetadata, INotebookKernelInfo, IInsetRenderOutput, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { ICompositeCodeEditor, IEditor } from 'vs/editor/common/editorCommon'; import { NotImplementedError } from 'vs/base/common/errors'; @@ -65,6 +65,7 @@ export class TestNotebookEditor implements INotebookEditor { constructor( ) { } + setOptions(options: NotebookEditorOptions | undefined): Promise { throw new Error('Method not implemented.'); } @@ -78,7 +79,8 @@ export class TestNotebookEditor implements INotebookEditor { onDidChangeActiveCell: Event = new Emitter().event; onDidScroll = new Emitter().event; onWillDispose = new Emitter().event; - + onDidChangeVisibleRanges: Event = new Emitter().event; + visibleRanges: ICellRange[] = []; uri?: URI | undefined; textModel?: NotebookTextModel | undefined; diff --git a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts index 2911cf0ab90..70070cd869b 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts @@ -81,7 +81,8 @@ suite('NotebookCell#Document', function () { addedEditors: [{ documentUri: notebookUri, id: '_notebook_editor_0', - selections: [0] + selections: [0], + visibleRanges: [] }] }); extHostNotebooks.$acceptDocumentAndEditorsDelta({ newActiveEditor: '_notebook_editor_0' }); diff --git a/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts b/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts index a25ee16c3f4..f2ca94c32d5 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts @@ -74,7 +74,8 @@ suite('NotebookConcatDocument', function () { { documentUri: notebookUri, id: '_notebook_editor_0', - selections: [0] + selections: [0], + visibleRanges: [] } ] }); From d3a5dd9d4bca75e7bb75258dfba58b8d616949a2 Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 28 Aug 2020 13:23:28 -0700 Subject: [PATCH 677/736] re #102503. reveal range --- src/vs/vscode.proposed.d.ts | 18 +++++++++++ .../api/browser/mainThreadNotebook.ts | 30 +++++++++++++++++-- .../workbench/api/common/extHost.api.impl.ts | 3 +- .../workbench/api/common/extHost.protocol.ts | 8 ++++- .../workbench/api/common/extHostNotebook.ts | 4 +++ src/vs/workbench/api/common/extHostTypes.ts | 6 ++++ 6 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 370cb790ec6..1d202abf7f3 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1368,6 +1368,22 @@ declare module 'vscode' { readonly end: number; } + export enum NotebookEditorRevealType { + /** + * The range will be revealed with as little scrolling as possible. + */ + Default = 0, + /** + * The range will always be revealed in the center of the viewport. + */ + InCenter = 1, + /** + * If the range is outside the viewport, it will be revealed in the center of the viewport. + * Otherwise, it will be revealed with as little scrolling as possible. + */ + InCenterIfOutsideViewport = 2, + } + export interface NotebookEditor { /** * The document associated with this notebook editor. @@ -1429,6 +1445,8 @@ declare module 'vscode' { asWebviewUri(localResource: Uri): Uri; edit(callback: (editBuilder: NotebookEditorCellEdit) => void): Thenable; + + revealRange(range: NotebookCellRange, revealType?: NotebookEditorRevealType): void; } export interface NotebookOutputSelector { diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 4cca5edccdb..0654a82df22 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -17,10 +17,10 @@ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; -import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, DisplayOrderKey, ICellEditOperation, IEditor, INotebookDocumentFilter, INotebookKernelInfo, NotebookCellMetadata, NotebookCellOutputsSplice, NotebookDocumentMetadata, NOTEBOOK_DISPLAY_ORDER, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, DisplayOrderKey, ICellEditOperation, ICellRange, IEditor, INotebookDocumentFilter, INotebookKernelInfo, NotebookCellMetadata, NotebookCellOutputsSplice, NotebookDocumentMetadata, NOTEBOOK_DISPLAY_ORDER, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, MainContext, MainThreadNotebookShape, NotebookExtensionDescription } from '../common/extHost.protocol'; +import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, MainContext, MainThreadNotebookShape, NotebookEditorRevealType, NotebookExtensionDescription } from '../common/extHost.protocol'; class DocumentAndEditorState { static ofSets(before: Set, after: Set): { removed: T[], added: T[] } { @@ -612,6 +612,32 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo textModel?.handleUnknownChange(); } + async $tryRevealRange(id: string, range: ICellRange, revealType: NotebookEditorRevealType) { + const editor = this._notebookService.listNotebookEditors().find(editor => editor.getId() === id); + if (editor && editor.isNotebookEditor) { + const notebookEditor = editor as INotebookEditor; + const viewModel = notebookEditor.viewModel; + const cell = viewModel?.viewCells[range.start]; + if (!cell) { + return; + } + + switch (revealType) { + case NotebookEditorRevealType.Default: + notebookEditor.revealInView(cell); + break; + case NotebookEditorRevealType.InCenter: + notebookEditor.revealInCenter(cell); + break; + case NotebookEditorRevealType.InCenterIfOutsideViewport: + notebookEditor.revealInCenterIfOutsideViewport(cell); + break; + default: + break; + } + } + } + async $setStatusBarEntry(id: number, rawStatusBarEntry: INotebookCellStatusBarEntryDto): Promise { const statusBarEntry = { ...rawStatusBarEntry, diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index ab29d86e094..0b1649905eb 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1138,7 +1138,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I CellOutputKind: extHostTypes.CellOutputKind, NotebookCellRunState: extHostTypes.NotebookCellRunState, NotebookRunState: extHostTypes.NotebookRunState, - NotebookCellStatusBarAlignment: extHostTypes.NotebookCellStatusBarAlignment + NotebookCellStatusBarAlignment: extHostTypes.NotebookCellStatusBarAlignment, + NotebookEditorRevealType: extHostTypes.NotebookEditorRevealType }; }; } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index d4bdfcfa5c2..13141f6127d 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -720,6 +720,12 @@ export type NotebookCellOutputsSplice = [ IProcessedOutput[] ]; +export enum NotebookEditorRevealType { + Default = 0, + InCenter = 1, + InCenterIfOutsideViewport = 2, +} + export type INotebookCellStatusBarEntryDto = Dto; export interface MainThreadNotebookShape extends IDisposable { @@ -738,7 +744,7 @@ export interface MainThreadNotebookShape extends IDisposable { $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[]): Promise; $postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise; $setStatusBarEntry(id: number, statusBarEntry: INotebookCellStatusBarEntryDto): Promise; - + $tryRevealRange(id: string, range: ICellRange, revealType: NotebookEditorRevealType): Promise; $onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void; $onContentChange(resource: UriComponents, viewType: string): void; } diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 8aeeb8e0b98..44b746613a1 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -747,6 +747,10 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook return this._proxy.$tryApplyEdits(this.viewType, this.uri, editData.documentVersionId, compressedEdits); } + revealRange(range: vscode.NotebookCellRange, revealType?: extHostTypes.NotebookEditorRevealType) { + this._proxy.$tryRevealRange(this.id, range, revealType || extHostTypes.NotebookEditorRevealType.Default); + } + get viewColumn(): vscode.ViewColumn | undefined { return this._viewColumn; } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index bb8a0e560d3..d832779db22 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2785,6 +2785,12 @@ export enum NotebookCellStatusBarAlignment { Right = 2 } +export enum NotebookEditorRevealType { + Default = 0, + InCenter = 1, + InCenterIfOutsideViewport = 2 +} + //#endregion From f56b64cffbbd86ed346e2c7384d67a3b9d0fdc09 Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 28 Aug 2020 13:55:52 -0700 Subject: [PATCH 678/736] do not trigger outputs change when there is no real change. --- src/vs/workbench/api/common/extHostNotebook.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 44b746613a1..16962e50bd4 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -462,6 +462,10 @@ export class ExtHostNotebookDocument extends Disposable { return [diff.start, diff.deleteCount, outputs]; }); + if (!outputDtos.length) { + return; + } + await this._proxy.$spliceNotebookCellOutputs(this._viewType, this.uri, cell.handle, outputDtos); this._emitter.emitCellOutputsChange({ document: this.notebookDocument, From 37b438fe9aa2e3ca645e88daa36d7e87dd5a800d Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 28 Aug 2020 15:18:45 -0700 Subject: [PATCH 679/736] Avoid race conditions in handling webview output changes, queue all processing --- src/vs/base/common/async.ts | 19 +++++++ src/vs/base/test/common/async.test.ts | 18 +++++++ .../notebook/browser/notebookEditorWidget.ts | 50 ++++++++++++------- .../browser/view/renderers/codeCell.ts | 10 ++-- .../contrib/notebook/common/notebookCommon.ts | 8 ++- 5 files changed, 80 insertions(+), 25 deletions(-) diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 6925d74b686..bb39088be12 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -170,6 +170,25 @@ export class Sequencer { } } +export class SequencerByKey { + + private promiseMap = new Map>(); + + queue(key: TKey, promiseTask: ITask>): Promise { + const runningPromise = this.promiseMap.get(key) ?? Promise.resolve(); + const newPromise = runningPromise + .catch(() => { }) + .then(promiseTask) + .finally(() => { + if (this.promiseMap.get(key) === newPromise) { + this.promiseMap.delete(key); + } + }); + this.promiseMap.set(key, newPromise); + return newPromise; + } +} + /** * A helper to delay execution of a task that is being requested often. * diff --git a/src/vs/base/test/common/async.test.ts b/src/vs/base/test/common/async.test.ts index 25e7d83cc7b..9d133c91dfa 100644 --- a/src/vs/base/test/common/async.test.ts +++ b/src/vs/base/test/common/async.test.ts @@ -688,4 +688,22 @@ suite('Async', () => { assert.ok(Date.now() - now < 100); assert.equal(timedout, false); }); + + test('SequencerByKey', async () => { + const s = new async.SequencerByKey(); + + const r1 = await s.queue('key1', () => Promise.resolve('hello')); + assert.equal(r1, 'hello'); + + await s.queue('key2', () => Promise.reject(new Error('failed'))).then(() => { + throw new Error('should not be resolved'); + }, err => { + // Expected error + assert.equal(err.message, 'failed'); + }); + + // Still works after a queued promise is rejected + const r3 = await s.queue('key2', () => Promise.resolve('hello')); + assert.equal(r3, 'hello'); + }); }); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index e65eca7edf8..bffba22197b 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -8,6 +8,7 @@ import * as DOM from 'vs/base/browser/dom'; import { IMouseWheelEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; import { IAction, Separator } from 'vs/base/common/actions'; +import { SequencerByKey } from 'vs/base/common/async'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Color, RGBA } from 'vs/base/common/color'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -51,7 +52,7 @@ import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewMod import { NotebookEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { CellViewModel, IModelDecorationsChangeAccessor, INotebookEditorViewState, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellKind, CellToolbarLocKey, ICellRange, IInsetRenderOutput, INotebookKernelInfo, INotebookKernelInfo2, INotebookKernelInfoDto, IProcessedOutput, NotebookCellRunState, NotebookRunState, ShowCellStatusbarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellToolbarLocKey, ICellRange, IInsetRenderOutput, INotebookKernelInfo, INotebookKernelInfo2, INotebookKernelInfoDto, IProcessedOutput, isTransformedDisplayOutput, NotebookCellRunState, NotebookRunState, ShowCellStatusbarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { editorGutterModifiedBackground } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; @@ -101,6 +102,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private readonly _onWillDispose = this._register(new Emitter()); public readonly onWillDispose: Event = this._onWillDispose.event; + private readonly _insetModifyQueueByOutputId = new SequencerByKey(); + set scrollTop(top: number) { if (this._list) { this._list.scrollTop = top; @@ -1577,30 +1580,37 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._list?.triggerScrollFromMouseWheelEvent(event); } - async createInset(cell: CodeCellViewModel, output: IInsetRenderOutput, offset: number) { - if (!this._webview) { - return; - } + async createInset(cell: CodeCellViewModel, output: IInsetRenderOutput, offset: number): Promise { + this._insetModifyQueueByOutputId.queue(output.source.outputId, async () => { + if (!this._webview) { + return; + } - await this._resolveWebview(); + await this._resolveWebview(); - if (!this._webview!.insetMapping.has(output.source)) { - const cellTop = this._list?.getAbsoluteTopOfElement(cell) || 0; - await this._webview!.createInset(cell, output, cellTop, offset); - } else { - const cellTop = this._list?.getAbsoluteTopOfElement(cell) || 0; - const scrollTop = this._list?.scrollTop || 0; + if (!this._webview!.insetMapping.has(output.source)) { + const cellTop = this._list?.getAbsoluteTopOfElement(cell) || 0; + await this._webview!.createInset(cell, output, cellTop, offset); + } else { + const cellTop = this._list?.getAbsoluteTopOfElement(cell) || 0; + const scrollTop = this._list?.scrollTop || 0; - this._webview!.updateViewScrollTop(-scrollTop, true, [{ cell, output: output.source, cellTop }]); - } + this._webview!.updateViewScrollTop(-scrollTop, true, [{ cell, output: output.source, cellTop }]); + } + }); } removeInset(output: IProcessedOutput) { - if (!this._webview || !this._webviewResolved) { + if (!isTransformedDisplayOutput(output)) { return; } - this._webview!.removeInset(output); + this._insetModifyQueueByOutputId.queue(output.outputId, async () => { + if (!this._webview || !this._webviewResolved) { + return; + } + this._webview!.removeInset(output); + }); } hideInset(output: IProcessedOutput) { @@ -1608,7 +1618,13 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return; } - this._webview!.hideInset(output); + if (!isTransformedDisplayOutput(output)) { + return; + } + + this._insetModifyQueueByOutputId.queue(output.outputId, async () => { + this._webview!.hideInset(output); + }); } getOutputRenderer(): OutputRenderer { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index 9ed49e93300..2917b4b0ed1 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -33,8 +33,6 @@ export class CodeCell extends Disposable { private outputResizeListeners = new Map(); private outputElements = new Map(); - private modifyInsetQueue = Promise.resolve(); - constructor( private notebookEditor: INotebookEditor, private viewCell: CodeCellViewModel, @@ -173,7 +171,7 @@ export class CodeCell extends Disposable { removedKeys.push(key); // remove element from DOM this.templateData?.outputContainer?.removeChild(value.element); - this.modifyInsetQueue = this.modifyInsetQueue.finally(() => this.notebookEditor.removeInset(key)); + this.notebookEditor.removeInset(key); } }); @@ -326,7 +324,7 @@ export class CodeCell extends Disposable { const renderedOutput = this.outputElements.get(currOutput); if (renderedOutput) { if (renderedOutput.renderResult.type !== RenderOutputType.None) { - this.modifyInsetQueue = this.modifyInsetQueue.finally(() => this.notebookEditor.createInset(this.viewCell, renderedOutput.renderResult as IInsetRenderOutput, this.viewCell.getOutputOffset(index))); + this.notebookEditor.createInset(this.viewCell, renderedOutput.renderResult as IInsetRenderOutput, this.viewCell.getOutputOffset(index)); } else { // Anything else, just update the height this.viewCell.updateOutputHeight(index, renderedOutput.element.clientHeight); @@ -515,7 +513,7 @@ export class CodeCell extends Disposable { if (result.type !== RenderOutputType.None) { this.viewCell.selfSizeMonitoring = true; - this.modifyInsetQueue = this.modifyInsetQueue.finally(() => this.notebookEditor.createInset(this.viewCell, result as any, this.viewCell.getOutputOffset(index))); + this.notebookEditor.createInset(this.viewCell, result as any, this.viewCell.getOutputOffset(index)); } else { DOM.addClass(outputItemDiv, 'foreground'); DOM.addClass(outputItemDiv, 'output-element'); @@ -610,7 +608,7 @@ export class CodeCell extends Disposable { const element = this.outputElements.get(output)?.element; if (element) { this.templateData?.outputContainer?.removeChild(element); - await (this.modifyInsetQueue = this.modifyInsetQueue.finally(() => this.notebookEditor.removeInset(output))); + this.notebookEditor.removeInset(output); } output.pickedMimeTypeIndex = pick; diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index c6be8ff48ab..2cf77f6ce69 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -215,6 +215,10 @@ export interface ITransformedDisplayOutputDto { pickedMimeTypeIndex?: number; } +export function isTransformedDisplayOutput(thing: unknown): thing is ITransformedDisplayOutputDto { + return (thing as ITransformedDisplayOutputDto).outputKind === CellOutputKind.Rich && !!(thing as ITransformedDisplayOutputDto).outputId; +} + export interface IGenericOutput { outputKind: CellOutputKind; pickedMimeType?: string; @@ -313,14 +317,14 @@ export interface IRenderNoOutput { export interface IRenderPlainHtmlOutput { type: RenderOutputType.Html; - source: IProcessedOutput; + source: ITransformedDisplayOutputDto; htmlContent: string; hasDynamicHeight: boolean; } export interface IRenderOutputViaExtension { type: RenderOutputType.Extension; - source: IProcessedOutput; + source: ITransformedDisplayOutputDto; mimeType: string; renderer: INotebookRendererInfo; } From b9f03c8c4adae1c071d592073537c934533ad89a Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 28 Aug 2020 15:47:18 -0700 Subject: [PATCH 680/736] Change cell collapse keybindings Fix #104860 --- .../contrib/notebook/browser/contrib/coreActions.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 55419ec854e..fdd17f4c6a0 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -1550,7 +1550,7 @@ registerAction2(class extends NotebookCellAction { title: localize('notebookActions.collapseCellInput', "Collapse Cell Input"), keybinding: { when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_INPUT_COLLAPSED.toNegated(), InputFocusedContext.toNegated()), - primary: KeyChord(KeyCode.KEY_C, KeyCode.KEY_C), + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_C), weight: KeybindingWeight.WorkbenchContrib }, menu: { @@ -1573,7 +1573,7 @@ registerAction2(class extends NotebookCellAction { title: localize('notebookActions.expandCellContent', "Expand Cell Content"), keybinding: { when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_INPUT_COLLAPSED), - primary: KeyChord(KeyCode.KEY_C, KeyCode.KEY_C), + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_C), weight: KeybindingWeight.WorkbenchContrib }, menu: { @@ -1596,7 +1596,7 @@ registerAction2(class extends NotebookCellAction { title: localize('notebookActions.collapseCellOutput', "Collapse Cell Output"), keybinding: { when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_OUTPUT_COLLAPSED.toNegated(), InputFocusedContext.toNegated(), NOTEBOOK_CELL_HAS_OUTPUTS), - primary: KeyChord(KeyCode.KEY_C, KeyCode.KEY_O), + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_T), weight: KeybindingWeight.WorkbenchContrib }, menu: { @@ -1619,7 +1619,7 @@ registerAction2(class extends NotebookCellAction { title: localize('notebookActions.expandCellOutput', "Expand Cell Output"), keybinding: { when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_OUTPUT_COLLAPSED), - primary: KeyChord(KeyCode.KEY_C, KeyCode.KEY_O), + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_T), weight: KeybindingWeight.WorkbenchContrib }, menu: { From c6e64d221790edb33e8499403e7f90da3fa78860 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 28 Aug 2020 16:32:30 -0700 Subject: [PATCH 681/736] Resolve relative image paths in markdown output Fix #104529 --- .../notebook/browser/notebookBrowser.ts | 2 +- .../browser/view/output/outputRenderer.ts | 5 ++-- .../view/output/transforms/richTransform.ts | 30 +++++++++---------- .../browser/view/renderers/codeCell.ts | 13 +++++--- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 05194d40170..6d6685bba57 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -569,7 +569,7 @@ export interface IOutputTransformContribution { * This call is allowed to have side effects, such as placing output * directly into the container element. */ - render(output: IProcessedOutput, container: HTMLElement, preferredMimeType: string | undefined): IRenderOutput; + render(output: IProcessedOutput, container: HTMLElement, preferredMimeType: string | undefined, notebookUri: URI | undefined): IRenderOutput; } export interface CellFindMatch { diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer.ts index 56114e323a5..41a457e3dc6 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer.ts @@ -8,6 +8,7 @@ import { IProcessedOutput, IRenderOutput, RenderOutputType } from 'vs/workbench/ import { NotebookRegistry } from 'vs/workbench/contrib/notebook/browser/notebookRegistry'; import { onUnexpectedError } from 'vs/base/common/errors'; import { INotebookEditor, IOutputTransformContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { URI } from 'vs/base/common/uri'; export class OutputRenderer { protected readonly _contributions: { [key: string]: IOutputTransformContribution; }; @@ -41,11 +42,11 @@ export class OutputRenderer { return { type: RenderOutputType.None, hasDynamicHeight: false }; } - render(output: IProcessedOutput, container: HTMLElement, preferredMimeType: string | undefined): IRenderOutput { + render(output: IProcessedOutput, container: HTMLElement, preferredMimeType: string | undefined, notebookUri: URI | undefined): IRenderOutput { const transform = this._mimeTypeMapping[output.outputKind]; if (transform) { - return transform.render(output, container, preferredMimeType); + return transform.render(output, container, preferredMimeType, notebookUri); } else { return this.renderNoop(output, container); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts index 76ffcc3f352..b7276425242 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts @@ -17,10 +17,10 @@ import { URI } from 'vs/base/common/uri'; import { MarkdownRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/mdRenderer'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { handleANSIOutput } from 'vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform'; +import { dirname } from 'vs/base/common/resources'; class RichRenderer implements IOutputTransformContribution { - private _mdRenderer: MarkdownRenderer; - private _richMimeTypeRenderers = new Map IRenderOutput>(); + private _richMimeTypeRenderers = new Map IRenderOutput>(); constructor( public notebookEditor: INotebookEditor, @@ -29,7 +29,6 @@ class RichRenderer implements IOutputTransformContribution { @IModeService private readonly modeService: IModeService, @IThemeService private readonly themeService: IThemeService ) { - this._mdRenderer = instantiationService.createInstance(MarkdownRenderer, undefined); this._richMimeTypeRenderers.set('application/json', this.renderJSON.bind(this)); this._richMimeTypeRenderers.set('application/javascript', this.renderJavaScript.bind(this)); this._richMimeTypeRenderers.set('text/html', this.renderHTML.bind(this)); @@ -41,7 +40,7 @@ class RichRenderer implements IOutputTransformContribution { this._richMimeTypeRenderers.set('text/x-javascript', this.renderCode.bind(this)); } - render(output: ITransformedDisplayOutputDto, container: HTMLElement, preferredMimeType: string | undefined): IRenderOutput { + render(output: ITransformedDisplayOutputDto, container: HTMLElement, preferredMimeType: string | undefined, notebookUri: URI): IRenderOutput { if (!output.data) { const contentNode = document.createElement('p'); contentNode.innerText = `No data could be found for output.`; @@ -69,10 +68,10 @@ class RichRenderer implements IOutputTransformContribution { } const renderer = this._richMimeTypeRenderers.get(preferredMimeType); - return renderer!(output, container); + return renderer!(output, notebookUri, container); } - renderJSON(output: ITransformedDisplayOutputDto, container: HTMLElement): IRenderOutput { + renderJSON(output: ITransformedDisplayOutputDto, notebookUri: URI, container: HTMLElement): IRenderOutput { const data = output.data['application/json']; const str = JSON.stringify(data, null, '\t'); @@ -105,7 +104,7 @@ class RichRenderer implements IOutputTransformContribution { return { type: RenderOutputType.None, hasDynamicHeight: true }; } - renderCode(output: ITransformedDisplayOutputDto, container: HTMLElement): IRenderOutput { + renderCode(output: ITransformedDisplayOutputDto, notebookUri: URI, container: HTMLElement): IRenderOutput { const data = output.data['text/x-javascript']; const str = (isArray(data) ? data.join('') : data) as string; @@ -138,7 +137,7 @@ class RichRenderer implements IOutputTransformContribution { return { type: RenderOutputType.None, hasDynamicHeight: true }; } - renderJavaScript(output: ITransformedDisplayOutputDto, container: HTMLElement): IRenderOutput { + renderJavaScript(output: ITransformedDisplayOutputDto, notebookUri: URI, container: HTMLElement): IRenderOutput { const data = output.data['application/javascript']; const str = isArray(data) ? data.join('') : data; const scriptVal = ``; @@ -150,7 +149,7 @@ class RichRenderer implements IOutputTransformContribution { }; } - renderHTML(output: ITransformedDisplayOutputDto, container: HTMLElement): IRenderOutput { + renderHTML(output: ITransformedDisplayOutputDto, notebookUri: URI, container: HTMLElement): IRenderOutput { const data = output.data['text/html']; const str = (isArray(data) ? data.join('') : data) as string; return { @@ -161,7 +160,7 @@ class RichRenderer implements IOutputTransformContribution { }; } - renderSVG(output: ITransformedDisplayOutputDto, container: HTMLElement): IRenderOutput { + renderSVG(output: ITransformedDisplayOutputDto, notebookUri: URI, container: HTMLElement): IRenderOutput { const data = output.data['image/svg+xml']; const str = (isArray(data) ? data.join('') : data) as string; return { @@ -172,17 +171,18 @@ class RichRenderer implements IOutputTransformContribution { }; } - renderMarkdown(output: ITransformedDisplayOutputDto, container: HTMLElement): IRenderOutput { + renderMarkdown(output: ITransformedDisplayOutputDto, notebookUri: URI, container: HTMLElement): IRenderOutput { const data = output.data['text/markdown']; const str = (isArray(data) ? data.join('') : data) as string; const mdOutput = document.createElement('div'); - mdOutput.appendChild(this._mdRenderer.render({ value: str, isTrusted: true, supportThemeIcons: true }).element); + const mdRenderer = this.instantiationService.createInstance(MarkdownRenderer, dirname(notebookUri)); + mdOutput.appendChild(mdRenderer.render({ value: str, isTrusted: true, supportThemeIcons: true }).element); container.appendChild(mdOutput); return { type: RenderOutputType.None, hasDynamicHeight: true }; } - renderPNG(output: ITransformedDisplayOutputDto, container: HTMLElement): IRenderOutput { + renderPNG(output: ITransformedDisplayOutputDto, notebookUri: URI, container: HTMLElement): IRenderOutput { const image = document.createElement('img'); image.src = `data:image/png;base64,${output.data['image/png']}`; const display = document.createElement('div'); @@ -192,7 +192,7 @@ class RichRenderer implements IOutputTransformContribution { return { type: RenderOutputType.None, hasDynamicHeight: true }; } - renderJPEG(output: ITransformedDisplayOutputDto, container: HTMLElement): IRenderOutput { + renderJPEG(output: ITransformedDisplayOutputDto, notebookUri: URI, container: HTMLElement): IRenderOutput { const image = document.createElement('img'); image.src = `data:image/jpeg;base64,${output.data['image/jpeg']}`; const display = document.createElement('div'); @@ -202,7 +202,7 @@ class RichRenderer implements IOutputTransformContribution { return { type: RenderOutputType.None, hasDynamicHeight: true }; } - renderPlainText(output: ITransformedDisplayOutputDto, container: HTMLElement): IRenderOutput { + renderPlainText(output: ITransformedDisplayOutputDto, notebookUri: URI, container: HTMLElement): IRenderOutput { const data = output.data['text/plain']; const str = (isArray(data) ? data.join('') : data) as string; const contentNode = DOM.$('.output-plaintext'); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index 2917b4b0ed1..64977d72610 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -9,6 +9,7 @@ import { raceCancellation } from 'vs/base/common/async'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; import { IDimension } from 'vs/editor/common/editorCommon'; import { IModeService } from 'vs/editor/common/services/modeService'; import * as nls from 'vs/nls'; @@ -17,7 +18,7 @@ import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/ import { CellFocusMode, CodeCellRenderTemplate, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; -import { BUILTIN_RENDERER_ID, CellOutputKind, IInsetRenderOutput, IProcessedOutput, IRenderOutput, ITransformedDisplayOutputDto, outputHasDynamicHeight, RenderOutputType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { BUILTIN_RENDERER_ID, CellOutputKind, CellUri, IInsetRenderOutput, IProcessedOutput, IRenderOutput, ITransformedDisplayOutputDto, outputHasDynamicHeight, RenderOutputType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; interface IMimeTypeRenderer extends IQuickPickItem { @@ -436,6 +437,10 @@ export class CodeCell extends Disposable { ); } + private getNotebookUri(): URI | undefined { + return CellUri.parse(this.viewCell.uri)?.notebook; + } + private renderOutput(currOutput: IProcessedOutput, index: number, beforeElement?: HTMLElement) { if (this.viewCell.metadata.outputCollapsed) { return; @@ -486,16 +491,16 @@ export class CodeCell extends Disposable { const renderer = this.notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId); result = renderer ? { type: RenderOutputType.Extension, renderer, source: currOutput, mimeType: pickedMimeTypeRenderer.mimeType } - : this.notebookEditor.getOutputRenderer().render(currOutput, innerContainer, pickedMimeTypeRenderer.mimeType); + : this.notebookEditor.getOutputRenderer().render(currOutput, innerContainer, pickedMimeTypeRenderer.mimeType, this.getNotebookUri(),); } else { - result = this.notebookEditor.getOutputRenderer().render(currOutput, innerContainer, pickedMimeTypeRenderer.mimeType); + result = this.notebookEditor.getOutputRenderer().render(currOutput, innerContainer, pickedMimeTypeRenderer.mimeType, this.getNotebookUri(),); } } else { // for text and error, there is no mimetype const innerContainer = DOM.$('.output-inner-container'); DOM.append(outputItemDiv, innerContainer); - result = this.notebookEditor.getOutputRenderer().render(currOutput, innerContainer, undefined); + result = this.notebookEditor.getOutputRenderer().render(currOutput, innerContainer, undefined, this.getNotebookUri(),); } if (!result) { From 054395c1535d9843fb7489b8bb1e3ef6c2a9b660 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 28 Aug 2020 17:43:39 -0700 Subject: [PATCH 682/736] Only show cell focus border when the notebook editor is focused --- .../contrib/notebook/browser/notebookEditorWidget.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index bffba22197b..31bbff9ce0f 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1875,10 +1875,10 @@ registerThemingParticipant((theme, collector) => { } const focusedCellBorderColor = theme.getColor(focusedCellBorder); - collector.addRule(`.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-top:before, - .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-bottom:before, - .monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:before, - .monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:after { + collector.addRule(`.monaco-workbench .notebookOverlay .monaco-list:focus .monaco-list-row.focused .cell-focus-indicator-top:before, + .monaco-workbench .notebookOverlay .monaco-list:focus .monaco-list-row.focused .cell-focus-indicator-bottom:before, + .monaco-workbench .notebookOverlay .monaco-list:focus .markdown-cell-row.focused:before, + .monaco-workbench .notebookOverlay .monaco-list:focus .markdown-cell-row.focused:after { border-color: ${focusedCellBorderColor} !important; }`); From 6a8c04d040eb409bbe03b3607ddf4abd77e72a74 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 28 Aug 2020 18:16:34 -0700 Subject: [PATCH 683/736] Show cell border with focus within the list, not on the list --- .../contrib/notebook/browser/notebookEditorWidget.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 31bbff9ce0f..708fe963edd 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1875,10 +1875,10 @@ registerThemingParticipant((theme, collector) => { } const focusedCellBorderColor = theme.getColor(focusedCellBorder); - collector.addRule(`.monaco-workbench .notebookOverlay .monaco-list:focus .monaco-list-row.focused .cell-focus-indicator-top:before, - .monaco-workbench .notebookOverlay .monaco-list:focus .monaco-list-row.focused .cell-focus-indicator-bottom:before, - .monaco-workbench .notebookOverlay .monaco-list:focus .markdown-cell-row.focused:before, - .monaco-workbench .notebookOverlay .monaco-list:focus .markdown-cell-row.focused:after { + collector.addRule(`.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-focus-indicator-top:before, + .monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-focus-indicator-bottom:before, + .monaco-workbench .notebookOverlay .monaco-list:focus-within .markdown-cell-row.focused:before, + .monaco-workbench .notebookOverlay .monaco-list:focus-within .markdown-cell-row.focused:after { border-color: ${focusedCellBorderColor} !important; }`); From aa71228026fd4c030b36b46ba7705db78730bc44 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 28 Aug 2020 18:17:27 -0700 Subject: [PATCH 684/736] Restore previous focus and DOM focus when undoing a cell insertion Fix #99151 --- .../contrib/notebook/browser/notebookEditorWidget.ts | 3 ++- .../contrib/notebook/browser/view/notebookCellList.ts | 4 ++++ .../contrib/notebook/browser/viewModel/notebookViewModel.ts | 5 +++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 708fe963edd..547b36d2195 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1264,7 +1264,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor const insertIndex = cell ? (direction === 'above' ? index : nextIndex) : index; - const newCell = this._notebookViewModel!.createCell(insertIndex, initialText, language, type, undefined, true); + const focused = this._list?.getFocusedElements(); + const newCell = this._notebookViewModel!.createCell(insertIndex, initialText, language, type, undefined, true, undefined, focused); return newCell as CellViewModel; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index 01984839042..f9846096655 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -475,7 +475,11 @@ export class NotebookCellList extends WorkbenchList implements ID return; } + const focusInside = DOM.isAncestor(document.activeElement, this.rowsContainer); super.splice(start, deleteCount, elements); + if (focusInside) { + this.domFocus(); + } const selectionsLeft = []; this._viewModel!.selectionHandles.forEach(handle => { diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts index 64db881ca8e..44d4d664aac 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts @@ -609,8 +609,9 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD return result; } - createCell(index: number, source: string, language: string, type: CellKind, metadata: NotebookCellMetadata | undefined, synchronous: boolean, pushUndoStop: boolean = true) { - this._notebook.createCell2(index, source, language, type, metadata, synchronous, pushUndoStop, undefined, undefined); + createCell(index: number, source: string, language: string, type: CellKind, metadata: NotebookCellMetadata | undefined, synchronous: boolean, pushUndoStop: boolean = true, previouslyFocused: ICellViewModel[] = []) { + const beforeSelections = previouslyFocused.map(e => e.handle); + this._notebook.createCell2(index, source, language, type, metadata, synchronous, pushUndoStop, beforeSelections, undefined); // TODO, rely on createCell to be sync return this.viewCells[index]; } From 292c8574c7bff96545790465032041419b1165c0 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sat, 29 Aug 2020 09:49:29 +0200 Subject: [PATCH 685/736] show web extensions in a separate section --- .../contrib/extensions/browser/extensionsViewlet.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index cd57561e753..33ab9f5a25e 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -128,14 +128,18 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio if (this.extensionManagementServerService.localExtensionManagementServer) { servers.push(this.extensionManagementServerService.localExtensionManagementServer); } + if (this.extensionManagementServerService.webExtensionManagementServer) { + servers.push(this.extensionManagementServerService.webExtensionManagementServer); + } if (this.extensionManagementServerService.remoteExtensionManagementServer) { servers.push(this.extensionManagementServerService.remoteExtensionManagementServer); } - if (servers.length === 0 && this.extensionManagementServerService.webExtensionManagementServer) { - servers.push(this.extensionManagementServerService.webExtensionManagementServer); - } const getViewName = (viewTitle: string, server: IExtensionManagementServer): string => { - return servers.length > 1 ? `${server.label} - ${viewTitle}` : viewTitle; + if (servers.length) { + const serverLabel = server === this.extensionManagementServerService.webExtensionManagementServer && !this.extensionManagementServerService.localExtensionManagementServer ? localize('local', "Local") : server.label; + return servers.length > 1 ? `${serverLabel} - ${viewTitle}` : viewTitle; + } + return viewTitle; }; for (const server of servers) { const getInstalledViewName = (): string => getViewName(localize('installed', "Installed"), server); From 113fea1d2ec6d44094bd4a2e87ecbce2cb151c9d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sat, 29 Aug 2020 11:08:52 +0200 Subject: [PATCH 686/736] Fix #103238 --- .../userDataSync/browser/userDataSyncWorkbenchService.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index 72286eeed77..38a28f9b45b 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -143,10 +143,8 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat await Promise.all(unregisteredProviders.map(({ id }) => this.extensionService.activateByEvent(getAuthenticationProviderActivationEvent(id)))); } - /* wait until all providers are availabe */ - if (this._authenticationProviders.some(({ id }) => !this.authenticationService.isAuthenticationProviderRegistered(id))) { - await Event.toPromise(Event.filter(this.authenticationService.onDidRegisterAuthenticationProvider, () => this._authenticationProviders.every(({ id }) => this.authenticationService.isAuthenticationProviderRegistered(id)))); - } + /* wait until one of the providers is availabe */ + await Event.toPromise(Event.filter(this.authenticationService.onDidRegisterAuthenticationProvider, ({ id }) => this.isSupportedAuthenticationProviderId(id))); /* initialize */ await this.initialize(); From a8cb99a6b161712f78a9debeeed0c622e360cc7e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sat, 29 Aug 2020 11:09:32 +0200 Subject: [PATCH 687/736] Fix #103405 --- .../platform/extensions/common/extensions.ts | 4 ++ .../extensions/browser/extensionsActions.ts | 2 +- .../common/extensionEnablementService.ts | 50 +++++++++++++++++-- .../common/extensionManagement.ts | 5 ++ .../extensionEnablementService.test.ts | 37 +++++++++++++- 5 files changed, 91 insertions(+), 7 deletions(-) diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index 73c21c8824f..b11f8baf57a 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -274,6 +274,10 @@ export function isLanguagePackExtension(manifest: IExtensionManifest): boolean { return manifest.contributes && manifest.contributes.localizations ? manifest.contributes.localizations.length > 0 : false; } +export function isAuthenticaionProviderExtension(manifest: IExtensionManifest): boolean { + return manifest.contributes && manifest.contributes.authentication ? manifest.contributes.authentication.length > 0 : false; +} + export interface IScannedExtension { readonly identifier: IExtensionIdentifier; readonly location: URI; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 70f25aa91b9..e5107d09ec2 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -970,7 +970,7 @@ export class DisableForWorkspaceAction extends ExtensionAction { if (this.extension && this.extension.local && this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier) && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY)) { this.enabled = this.extension.state === ExtensionState.Installed && (this.extension.enablementState === EnablementState.EnabledGlobally || this.extension.enablementState === EnablementState.EnabledWorkspace) - && this.extensionEnablementService.canChangeEnablement(this.extension.local); + && this.extensionEnablementService.canChangeWorkspaceEnablement(this.extension.local); } } diff --git a/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts index c28b1477400..c57b798d290 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts @@ -12,13 +12,14 @@ import { areSameExtensions } from 'vs/platform/extensionManagement/common/extens import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { ExtensionType, IExtension } from 'vs/platform/extensions/common/extensions'; +import { ExtensionType, IExtension, isAuthenticaionProviderExtension, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IProductService } from 'vs/platform/product/common/productService'; import { StorageManager } from 'vs/platform/extensionManagement/common/extensionEnablementService'; import { webWorkerExtHostConfig } from 'vs/workbench/services/extensions/common/extensions'; +import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; const SOURCE = 'IWorkbenchExtensionEnablementService'; @@ -40,6 +41,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench @IConfigurationService private readonly configurationService: IConfigurationService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @IProductService private readonly productService: IProductService, + @IUserDataSyncAccountService private readonly userDataSyncAccountService: IUserDataSyncAccountService, ) { super(); this.storageManger = this._register(new StorageManager(storageService)); @@ -66,7 +68,9 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench } canChangeEnablement(extension: IExtension): boolean { - if (extension.manifest && extension.manifest.contributes && extension.manifest.contributes.localizations && extension.manifest.contributes.localizations.length) { + try { + this.throwErrorIfCannotChangeEnablement(extension); + } catch (error) { return false; } const enablementState = this.getEnablementState(extension); @@ -76,11 +80,47 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench return true; } + private throwErrorIfCannotChangeEnablement(extension: IExtension): void { + if (isLanguagePackExtension(extension.manifest)) { + throw new Error(localize('cannot disable language pack extension', "Cannot disable {0} extension because it contributes language packs.", extension.manifest.displayName || extension.identifier.id)); + } + + if (this.userDataSyncAccountService.account && + isAuthenticaionProviderExtension(extension.manifest) && extension.manifest.contributes!.authentication!.some(a => a.id === this.userDataSyncAccountService.account!.authenticationProviderId)) { + throw new Error(localize('cannot disable auth extension', "Cannot disable {0} extension because Settings Sync depends on it.", extension.manifest.displayName || extension.identifier.id)); + } + } + + canChangeWorkspaceEnablement(extension: IExtension): boolean { + if (!this.canChangeEnablement(extension)) { + return false; + } + try { + this.throwErrorIfCannotChangeWorkspaceEnablement(extension); + } catch (error) { + return false; + } + return true; + } + + private throwErrorIfCannotChangeWorkspaceEnablement(extension: IExtension): void { + if (!this.hasWorkspace) { + throw new Error(localize('noWorkspace', "No workspace.")); + } + if (isAuthenticaionProviderExtension(extension.manifest)) { + throw new Error(localize('cannot disable auth extension in workspace', "Cannot disable {0} extension in workspace because it contributes authentication providers", extension.manifest.displayName || extension.identifier.id)); + } + } + async setEnablement(extensions: IExtension[], newState: EnablementState): Promise { const workspace = newState === EnablementState.DisabledWorkspace || newState === EnablementState.EnabledWorkspace; - if (workspace && !this.hasWorkspace) { - return Promise.reject(new Error(localize('noWorkspace', "No workspace."))); + for (const extension of extensions) { + if (workspace) { + this.throwErrorIfCannotChangeWorkspaceEnablement(extension); + } else { + this.throwErrorIfCannotChangeEnablement(extension); + } } const result = await Promise.all(extensions.map(e => this._setEnablement(e, newState))); @@ -316,4 +356,4 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench } } -registerSingleton(IWorkbenchExtensionEnablementService, ExtensionEnablementService, true); +registerSingleton(IWorkbenchExtensionEnablementService, ExtensionEnablementService); diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts index 12c949d67d2..c2e854e837d 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts @@ -58,6 +58,11 @@ export interface IWorkbenchExtensionEnablementService { */ canChangeEnablement(extension: IExtension): boolean; + /** + * Returns `true` if the enablement can be changed. + */ + canChangeWorkspaceEnablement(extension: IExtension): boolean; + /** * Returns `true` if the given extension identifier is enabled. */ diff --git a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts index d7ccb3fafec..8936f321c93 100644 --- a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts +++ b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts @@ -23,6 +23,7 @@ import { assign } from 'vs/base/common/objects'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { productService } from 'vs/workbench/test/browser/workbenchTestServices'; import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; +import { IUserDataSyncAccountService, UserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; function createStorageService(instantiationService: TestInstantiationService): IStorageService { let service = instantiationService.get(IStorageService); @@ -51,7 +52,8 @@ export class TestExtensionEnablementService extends ExtensionEnablementService { extensionManagementService, instantiationService.get(IConfigurationService), extensionManagementServerService, - productService + productService, + instantiationService.get(IUserDataSyncAccountService) || instantiationService.stub(IUserDataSyncAccountService, UserDataSyncAccountService) ); } @@ -371,6 +373,39 @@ suite('ExtensionEnablementService Test', () => { assert.equal(testObject.canChangeEnablement(aLocalExtension('pub.a', { localizations: [{ languageId: 'gr', translations: [{ id: 'vscode', path: 'path' }] }] })), false); }); + test('test canChangeEnablement return true for auth extension', () => { + assert.equal(testObject.canChangeEnablement(aLocalExtension('pub.a', { authentication: [{ id: 'a', label: 'a' }] })), true); + }); + + test('test canChangeEnablement return true for auth extension when user data sync account does not depends on it', () => { + instantiationService.stub(IUserDataSyncAccountService, >{ + account: { authenticationProviderId: 'b' } + }); + testObject = new TestExtensionEnablementService(instantiationService); + assert.equal(testObject.canChangeEnablement(aLocalExtension('pub.a', { authentication: [{ id: 'a', label: 'a' }] })), true); + }); + + test('test canChangeEnablement return false for auth extension and user data sync account depends on it', () => { + instantiationService.stub(IUserDataSyncAccountService, >{ + account: { authenticationProviderId: 'a' } + }); + testObject = new TestExtensionEnablementService(instantiationService); + assert.equal(testObject.canChangeEnablement(aLocalExtension('pub.a', { authentication: [{ id: 'a', label: 'a' }] })), false); + }); + + test('test canChangeWorkspaceEnablement return true', () => { + assert.equal(testObject.canChangeWorkspaceEnablement(aLocalExtension('pub.a')), true); + }); + + test('test canChangeWorkspaceEnablement return false if there is no workspace', () => { + instantiationService.stub(IWorkspaceContextService, 'getWorkbenchState', WorkbenchState.EMPTY); + assert.equal(testObject.canChangeWorkspaceEnablement(aLocalExtension('pub.a')), false); + }); + + test('test canChangeWorkspaceEnablement return false for auth extension', () => { + assert.equal(testObject.canChangeWorkspaceEnablement(aLocalExtension('pub.a', { authentication: [{ id: 'a', label: 'a' }] })), false); + }); + test('test canChangeEnablement return false when extensions are disabled in environment', () => { instantiationService.stub(IWorkbenchEnvironmentService, { disableExtensions: true } as IWorkbenchEnvironmentService); testObject = new TestExtensionEnablementService(instantiationService); From b96ce60e2d5bbb8fbd68b1cd4357c3035d45767a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sat, 29 Aug 2020 12:14:46 +0200 Subject: [PATCH 688/736] #103405 check if sync is on --- .../extensions/browser/extensionsActions.ts | 2 +- .../browser/userDataAutoSyncService.ts | 42 ------------------- .../browser/userDataSync.contribution.ts | 2 + .../browser/userDataSyncTrigger.ts | 34 ++++++++++----- .../common/extensionEnablementService.ts | 4 +- .../extensionEnablementService.test.ts | 13 +++++- .../userDataAutoSyncService.ts | 9 ++-- .../userDataSyncStoreManagementService.ts | 5 ++- src/vs/workbench/workbench.desktop.main.ts | 7 +--- src/vs/workbench/workbench.web.main.ts | 6 +-- 10 files changed, 54 insertions(+), 70 deletions(-) delete mode 100644 src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService.ts rename src/vs/workbench/{contrib => services}/userDataSync/electron-browser/userDataAutoSyncService.ts (79%) rename src/vs/workbench/{contrib => services}/userDataSync/electron-browser/userDataSyncStoreManagementService.ts (87%) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index e5107d09ec2..6d18b730543 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -909,7 +909,7 @@ export class EnableForWorkspaceAction extends ExtensionAction { if (this.extension && this.extension.local) { this.enabled = this.extension.state === ExtensionState.Installed && !this.extensionEnablementService.isEnabled(this.extension.local) - && this.extensionEnablementService.canChangeEnablement(this.extension.local); + && this.extensionEnablementService.canChangeWorkspaceEnablement(this.extension.local); } } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService.ts deleted file mode 100644 index 2c3b7295c73..00000000000 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService.ts +++ /dev/null @@ -1,42 +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 { IUserDataSyncService, IUserDataSyncLogService, IUserDataSyncResourceEnablementService, IUserDataSyncStoreService, IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync'; -import { Event } from 'vs/base/common/event'; -import { UserDataAutoSyncService as BaseUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { UserDataSyncTrigger } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger'; -import { IStorageService } from 'vs/platform/storage/common/storage'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; - -export class UserDataAutoSyncService extends BaseUserDataAutoSyncService { - - constructor( - @IUserDataSyncStoreManagementService userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, - @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, - @IUserDataSyncResourceEnablementService userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, - @IUserDataSyncService userDataSyncService: IUserDataSyncService, - @IUserDataSyncLogService logService: IUserDataSyncLogService, - @IUserDataSyncAccountService authTokenService: IUserDataSyncAccountService, - @IInstantiationService instantiationService: IInstantiationService, - @IHostService hostService: IHostService, - @ITelemetryService telemetryService: ITelemetryService, - @IUserDataSyncMachinesService userDataSyncMachinesService: IUserDataSyncMachinesService, - @IStorageService storageService: IStorageService, - @IEnvironmentService environmentService: IEnvironmentService, - ) { - super(userDataSyncStoreManagementService, userDataSyncStoreService, userDataSyncResourceEnablementService, userDataSyncService, logService, authTokenService, telemetryService, userDataSyncMachinesService, storageService, environmentService); - - this._register(Event.debounce(Event.any( - Event.map(hostService.onDidChangeFocus, () => 'windowFocus'), - instantiationService.createInstance(UserDataSyncTrigger).onDidTriggerSync, - ), (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerSync(sources, true, false))); - } - -} diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts index 9a055fc2d7a..8701765c53c 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts @@ -13,6 +13,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; import { isWeb } from 'vs/base/common/platform'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { UserDataSyncTrigger } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger'; class UserDataSyncReportIssueContribution extends Disposable implements IWorkbenchContribution { @@ -67,6 +68,7 @@ export class UserDataSyncSettingsMigrationContribution implements IWorkbenchCont const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(UserDataSyncWorkbenchContribution, LifecyclePhase.Ready); workbenchRegistry.registerWorkbenchContribution(UserDataSyncSettingsMigrationContribution, LifecyclePhase.Eventually); +workbenchRegistry.registerWorkbenchContribution(UserDataSyncTrigger, LifecyclePhase.Eventually); if (isWeb) { workbenchRegistry.registerWorkbenchContribution(UserDataSyncReportIssueContribution, LifecyclePhase.Ready); diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger.ts index 36caf6f3db4..9cb6e200cd3 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Emitter } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { SettingsEditor2Input, KeybindingsEditorInput, PreferencesEditorInput } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; @@ -12,24 +12,36 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { IEditorInput } from 'vs/workbench/common/editor'; import { IViewsService } from 'vs/workbench/common/views'; +import { IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { isWeb } from 'vs/base/common/platform'; +import { IHostService } from 'vs/workbench/services/host/browser/host'; -export class UserDataSyncTrigger extends Disposable { - - private readonly _onDidTriggerSync: Emitter = this._register(new Emitter()); - readonly onDidTriggerSync: Event = this._onDidTriggerSync.event; +export class UserDataSyncTrigger extends Disposable implements IWorkbenchContribution { constructor( @IEditorService editorService: IEditorService, @IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService, @IViewsService viewsService: IViewsService, + @IUserDataAutoSyncService userDataAutoSyncService: IUserDataAutoSyncService, + @IHostService hostService: IHostService, ) { super(); - this._register( - Event.filter( - Event.any( - Event.map(editorService.onDidActiveEditorChange, () => this.getUserDataEditorInputSource(editorService.activeEditor)), - Event.map(Event.filter(viewsService.onDidChangeViewContainerVisibility, e => e.id === VIEWLET_ID && e.visible), e => e.id) - ), source => source !== undefined)(source => this._onDidTriggerSync.fire(source!))); + const event = Event.filter( + Event.any( + Event.map(editorService.onDidActiveEditorChange, () => this.getUserDataEditorInputSource(editorService.activeEditor)), + Event.map(Event.filter(viewsService.onDidChangeViewContainerVisibility, e => e.id === VIEWLET_ID && e.visible), e => e.id) + ), source => source !== undefined); + if (isWeb) { + this._register(Event.debounce( + Event.any( + Event.map(hostService.onDidChangeFocus, () => 'windowFocus'), + Event.map(event, source => source!), + ), (last, source) => last ? [...last, source] : [source], 1000) + (sources => userDataAutoSyncService.triggerSync(sources, true, false))); + } else { + this._register(event(source => userDataAutoSyncService.triggerSync([source!], true, false))); + } } private getUserDataEditorInputSource(editorInput: IEditorInput | undefined): string | undefined { diff --git a/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts index c57b798d290..32f3dc52c1f 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts @@ -20,6 +20,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { StorageManager } from 'vs/platform/extensionManagement/common/extensionEnablementService'; import { webWorkerExtHostConfig } from 'vs/workbench/services/extensions/common/extensions'; import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; +import { IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync'; const SOURCE = 'IWorkbenchExtensionEnablementService'; @@ -41,6 +42,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench @IConfigurationService private readonly configurationService: IConfigurationService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @IProductService private readonly productService: IProductService, + @IUserDataAutoSyncService private readonly userDataAutoSyncService: IUserDataAutoSyncService, @IUserDataSyncAccountService private readonly userDataSyncAccountService: IUserDataSyncAccountService, ) { super(); @@ -85,7 +87,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench throw new Error(localize('cannot disable language pack extension', "Cannot disable {0} extension because it contributes language packs.", extension.manifest.displayName || extension.identifier.id)); } - if (this.userDataSyncAccountService.account && + if (this.userDataAutoSyncService.isEnabled() && this.userDataSyncAccountService.account && isAuthenticaionProviderExtension(extension.manifest) && extension.manifest.contributes!.authentication!.some(a => a.id === this.userDataSyncAccountService.account!.authenticationProviderId)) { throw new Error(localize('cannot disable auth extension', "Cannot disable {0} extension because Settings Sync depends on it.", extension.manifest.displayName || extension.identifier.id)); } diff --git a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts index 8936f321c93..79ba1b68a98 100644 --- a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts +++ b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts @@ -24,6 +24,7 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/ import { productService } from 'vs/workbench/test/browser/workbenchTestServices'; import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; import { IUserDataSyncAccountService, UserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; +import { IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync'; function createStorageService(instantiationService: TestInstantiationService): IStorageService { let service = instantiationService.get(IStorageService); @@ -53,6 +54,7 @@ export class TestExtensionEnablementService extends ExtensionEnablementService { instantiationService.get(IConfigurationService), extensionManagementServerService, productService, + instantiationService.get(IUserDataAutoSyncService) || instantiationService.stub(IUserDataAutoSyncService, >{ isEnabled() { return false; } }), instantiationService.get(IUserDataSyncAccountService) || instantiationService.stub(IUserDataSyncAccountService, UserDataSyncAccountService) ); } @@ -385,7 +387,16 @@ suite('ExtensionEnablementService Test', () => { assert.equal(testObject.canChangeEnablement(aLocalExtension('pub.a', { authentication: [{ id: 'a', label: 'a' }] })), true); }); - test('test canChangeEnablement return false for auth extension and user data sync account depends on it', () => { + test('test canChangeEnablement return true for auth extension when user data sync account depends on it but auto sync is off', () => { + instantiationService.stub(IUserDataSyncAccountService, >{ + account: { authenticationProviderId: 'a' } + }); + testObject = new TestExtensionEnablementService(instantiationService); + assert.equal(testObject.canChangeEnablement(aLocalExtension('pub.a', { authentication: [{ id: 'a', label: 'a' }] })), true); + }); + + test('test canChangeEnablement return false for auth extension and user data sync account depends on it and auto sync is on', () => { + instantiationService.stub(IUserDataAutoSyncService, >{ isEnabled() { return true; } }); instantiationService.stub(IUserDataSyncAccountService, >{ account: { authenticationProviderId: 'a' } }); diff --git a/src/vs/workbench/contrib/userDataSync/electron-browser/userDataAutoSyncService.ts b/src/vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService.ts similarity index 79% rename from src/vs/workbench/contrib/userDataSync/electron-browser/userDataAutoSyncService.ts rename to src/vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService.ts index 20ebeff1c5d..d48dbbdab49 100644 --- a/src/vs/workbench/contrib/userDataSync/electron-browser/userDataAutoSyncService.ts +++ b/src/vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService.ts @@ -7,13 +7,12 @@ import { IUserDataAutoSyncService, UserDataSyncError, IUserDataSyncStoreManageme import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event } from 'vs/base/common/event'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { UserDataSyncTrigger } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger'; import { UserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService implements IUserDataAutoSyncService { +class UserDataAutoSyncService extends UserDataAutoSyncEnablementService implements IUserDataAutoSyncService { declare readonly _serviceBrand: undefined; @@ -24,12 +23,10 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i @IStorageService storageService: IStorageService, @IEnvironmentService environmentService: IEnvironmentService, @IUserDataSyncStoreManagementService userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, - @IInstantiationService instantiationService: IInstantiationService, @ISharedProcessService sharedProcessService: ISharedProcessService, ) { super(storageService, environmentService, userDataSyncStoreManagementService); this.channel = sharedProcessService.getChannel('userDataAutoSync'); - this._register(instantiationService.createInstance(UserDataSyncTrigger).onDidTriggerSync(source => this.triggerSync([source], true, false))); } triggerSync(sources: string[], hasToLimitSync: boolean, disableCache: boolean): Promise { @@ -45,3 +42,5 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i } } + +registerSingleton(IUserDataAutoSyncService, UserDataAutoSyncService); diff --git a/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSyncStoreManagementService.ts b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncStoreManagementService.ts similarity index 87% rename from src/vs/workbench/contrib/userDataSync/electron-browser/userDataSyncStoreManagementService.ts rename to src/vs/workbench/services/userDataSync/electron-browser/userDataSyncStoreManagementService.ts index 4a34eee9101..d408711a038 100644 --- a/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSyncStoreManagementService.ts +++ b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncStoreManagementService.ts @@ -11,8 +11,9 @@ import { AbstractUserDataSyncStoreManagementService } from 'vs/platform/userData import { IProductService } from 'vs/platform/product/common/productService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { URI } from 'vs/base/common/uri'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -export class UserDataSyncStoreManagementService extends AbstractUserDataSyncStoreManagementService implements IUserDataSyncStoreManagementService { +class UserDataSyncStoreManagementService extends AbstractUserDataSyncStoreManagementService implements IUserDataSyncStoreManagementService { private readonly channel: IChannel; @@ -46,3 +47,5 @@ export class UserDataSyncStoreManagementService extends AbstractUserDataSyncStor } } + +registerSingleton(IUserDataSyncStoreManagementService, UserDataSyncStoreManagementService); diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index b436aed9f7a..8c642a03f3e 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -55,6 +55,8 @@ import 'vs/workbench/services/workspaces/electron-browser/workspaceEditingServic import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncMachinesService'; import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncService'; import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncAccountService'; +import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncStoreManagementService'; +import 'vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService'; import 'vs/workbench/services/sharedProcess/electron-browser/sharedProcessService'; import 'vs/workbench/services/localizations/electron-browser/localizationsService'; import 'vs/workbench/services/path/electron-browser/pathService'; @@ -63,8 +65,6 @@ import 'vs/workbench/services/experiment/electron-browser/experimentService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService'; -import { IUserDataAutoSyncService, IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync'; -import { UserDataAutoSyncService } from 'vs/workbench/contrib/userDataSync/electron-browser/userDataAutoSyncService'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; import { TunnelService } from 'vs/platform/remote/node/tunnelService'; import { ITimerService } from 'vs/workbench/services/timer/browser/timerService'; @@ -72,8 +72,6 @@ import { TimerService } from 'vs/workbench/services/timer/electron-browser/timer import { IUserDataInitializationService, UserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit'; registerSingleton(ICredentialsService, KeytarCredentialsService, true); -registerSingleton(IUserDataSyncStoreManagementService, UserDataSyncStoreManagementService); -registerSingleton(IUserDataAutoSyncService, UserDataAutoSyncService); registerSingleton(ITunnelService, TunnelService); registerSingleton(ITimerService, TimerService); registerSingleton(IUserDataInitializationService, UserDataInitializationService); @@ -137,6 +135,5 @@ import 'vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribu // Configuration Exporter import 'vs/workbench/contrib/configExporter/electron-browser/configurationExportHelper.contribution'; -import { UserDataSyncStoreManagementService } from 'vs/workbench/contrib/userDataSync/electron-browser/userDataSyncStoreManagementService'; //#endregion diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index e6452bb627c..f02bbbf874b 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -71,7 +71,7 @@ import { UserDataSyncStoreService, UserDataSyncStoreManagementService } from 'vs import { UserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSyncBackupStoreService'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; import { IUserDataSyncAccountService, UserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; -import { UserDataAutoSyncService } from 'vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService'; +import { UserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; import { AccessibilityService } from 'vs/platform/accessibility/common/accessibilityService'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { TitlebarPart } from 'vs/workbench/browser/parts/titlebar/titlebarPart'; @@ -90,8 +90,8 @@ registerSingleton(IUserDataSyncMachinesService, UserDataSyncMachinesService); registerSingleton(IUserDataSyncBackupStoreService, UserDataSyncBackupStoreService); registerSingleton(IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService); registerSingleton(IUserDataSyncAccountService, UserDataSyncAccountService); -registerSingleton(IUserDataAutoSyncService, UserDataAutoSyncService); -registerSingleton(IUserDataSyncService, UserDataSyncService); +registerSingleton(IUserDataSyncService, UserDataSyncService, true); +registerSingleton(IUserDataAutoSyncService, UserDataAutoSyncService, true); registerSingleton(ITitleService, TitlebarPart); registerSingleton(IExtensionTipsService, ExtensionTipsService); registerSingleton(ITimerService, TimerService); From edbf435008b28b2fa1a4e47792b95e1334025b02 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Sun, 30 Aug 2020 00:30:10 -0400 Subject: [PATCH 689/736] Fixes #104508 - adds 2px for window border --- src/vs/workbench/services/hover/browser/hoverWidget.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/hover/browser/hoverWidget.ts b/src/vs/workbench/services/hover/browser/hoverWidget.ts index 84e12a399ba..622d75418b6 100644 --- a/src/vs/workbench/services/hover/browser/hoverWidget.ts +++ b/src/vs/workbench/services/hover/browser/hoverWidget.ts @@ -16,6 +16,7 @@ import { HoverWidget as BaseHoverWidget, renderHoverAction } from 'vs/base/brows import { Widget } from 'vs/base/browser/ui/widget'; import { AnchorPosition } from 'vs/base/browser/ui/contextview/contextview'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; const $ = dom.$; @@ -48,7 +49,8 @@ export class HoverWidget extends Widget { options: IHoverOptions, @IKeybindingService private readonly _keybindingService: IKeybindingService, @IConfigurationService private readonly _configurationService: IConfigurationService, - @IOpenerService private readonly _openerService: IOpenerService + @IOpenerService private readonly _openerService: IOpenerService, + @IWorkbenchLayoutService private readonly _workbenchLayoutService: IWorkbenchLayoutService, ) { super(); @@ -141,7 +143,7 @@ export class HoverWidget extends Widget { // Get horizontal alignment and position let targetLeft = this._target.x !== undefined ? this._target.x : Math.min(...targetBounds.map(e => e.left)); if (targetLeft + this._hover.containerDomNode.clientWidth >= document.documentElement.clientWidth) { - this._x = document.documentElement.clientWidth - 1; + this._x = document.documentElement.clientWidth - (this._workbenchLayoutService.hasWindowBorder() ? 3 : 1); this._hover.containerDomNode.classList.add('right-aligned'); } else { this._x = targetLeft; From 5e457c791cec30a9bda1a4f6a7269fc70b8010fa Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Sun, 30 Aug 2020 01:13:07 -0400 Subject: [PATCH 690/736] Ensures titleDescription is always next to title --- src/vs/base/browser/dom.ts | 5 +++++ .../workbench/browser/parts/views/viewPaneContainer.ts | 10 +++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 0e254009357..9349efc2792 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -997,6 +997,11 @@ export function trackFocus(element: HTMLElement | Window): IFocusTracker { return new FocusTracker(element); } +export function after(sibling: HTMLElement, child: T): T { + sibling.after(child); + return child; +} + export function append(parent: HTMLElement, ...children: T[]): T { children.forEach(child => parent.appendChild(child)); return children[children.length - 1]; diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index fb3ca8ac474..39d2881dd70 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -9,7 +9,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { ColorIdentifier, activeContrastBorder, foreground } from 'vs/platform/theme/common/colorRegistry'; import { attachStyler, IColorMapping, attachButtonStyler, attachLinkStyler, attachProgressBarStyler } from 'vs/platform/theme/common/styler'; import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND, SIDE_BAR_SECTION_HEADER_BORDER, PANEL_BACKGROUND, SIDE_BAR_BACKGROUND, PANEL_SECTION_HEADER_FOREGROUND, PANEL_SECTION_HEADER_BACKGROUND, PANEL_SECTION_HEADER_BORDER, PANEL_SECTION_DRAG_AND_DROP_BACKGROUND, PANEL_SECTION_BORDER } from 'vs/workbench/common/theme'; -import { append, $, trackFocus, toggleClass, EventType, isAncestor, Dimension, addDisposableListener, removeClass, addClass, createCSSRule, asCSSUrl, addClasses } from 'vs/base/browser/dom'; +import { after, append, prepend, $, trackFocus, toggleClass, EventType, isAncestor, Dimension, addDisposableListener, removeClass, addClass, createCSSRule, asCSSUrl, addClasses } from 'vs/base/browser/dom'; import { IDisposable, combinedDisposable, dispose, toDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { firstIndex } from 'vs/base/common/arrays'; import { IAction, Separator, IActionViewItem } from 'vs/base/common/actions'; @@ -369,7 +369,7 @@ export abstract class ViewPane extends Pane implements IView { this.titleContainer = append(container, $('h3.title', undefined, calculatedTitle)); if (this._titleDescription) { - this.setTitleDescription(this._titleDescription, container); + this.setTitleDescription(this._titleDescription); } this.iconContainer.title = calculatedTitle; @@ -391,12 +391,12 @@ export abstract class ViewPane extends Pane implements IView { this._onDidChangeTitleArea.fire(); } - private setTitleDescription(description: string | undefined, headerContainer: HTMLElement | undefined = this.headerContainer) { + private setTitleDescription(description: string | undefined) { if (this.titleDescriptionContainer) { this.titleDescriptionContainer.textContent = description ?? ''; } - else if (description && headerContainer) { - this.titleDescriptionContainer = append(headerContainer, $('span.description', undefined, description)); + else if (description && this.titleContainer) { + this.titleDescriptionContainer = after(this.titleContainer, $('span.description', undefined, description)); } } From a87c4fa09018199fdd0025ebdf165fb1dbad05f8 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Sun, 30 Aug 2020 01:39:38 -0400 Subject: [PATCH 691/736] Removes unused import --- src/vs/workbench/browser/parts/views/viewPaneContainer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index 39d2881dd70..d718c7846ca 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -9,7 +9,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { ColorIdentifier, activeContrastBorder, foreground } from 'vs/platform/theme/common/colorRegistry'; import { attachStyler, IColorMapping, attachButtonStyler, attachLinkStyler, attachProgressBarStyler } from 'vs/platform/theme/common/styler'; import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND, SIDE_BAR_SECTION_HEADER_BORDER, PANEL_BACKGROUND, SIDE_BAR_BACKGROUND, PANEL_SECTION_HEADER_FOREGROUND, PANEL_SECTION_HEADER_BACKGROUND, PANEL_SECTION_HEADER_BORDER, PANEL_SECTION_DRAG_AND_DROP_BACKGROUND, PANEL_SECTION_BORDER } from 'vs/workbench/common/theme'; -import { after, append, prepend, $, trackFocus, toggleClass, EventType, isAncestor, Dimension, addDisposableListener, removeClass, addClass, createCSSRule, asCSSUrl, addClasses } from 'vs/base/browser/dom'; +import { after, append, $, trackFocus, toggleClass, EventType, isAncestor, Dimension, addDisposableListener, removeClass, addClass, createCSSRule, asCSSUrl, addClasses } from 'vs/base/browser/dom'; import { IDisposable, combinedDisposable, dispose, toDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { firstIndex } from 'vs/base/common/arrays'; import { IAction, Separator, IActionViewItem } from 'vs/base/common/actions'; From 936211bbcd4904df150ba734e58a9c60351228cf Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Sun, 30 Aug 2020 16:58:16 +0200 Subject: [PATCH 692/736] use absolute path for 'env'; fixes #105642 --- src/vs/workbench/contrib/debug/node/terminals.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/node/terminals.ts b/src/vs/workbench/contrib/debug/node/terminals.ts index 3c5fcd77c9a..0c27cb6d55c 100644 --- a/src/vs/workbench/contrib/debug/node/terminals.ts +++ b/src/vs/workbench/contrib/debug/node/terminals.ts @@ -177,7 +177,7 @@ export function prepareCommand(shell: string, args: string[], cwd?: string, env? command += `cd ${quote(cwd)} ; `; } if (env) { - command += 'env'; + command += '/usr/bin/env'; for (let key in env) { const value = env[key]; if (value === null) { From 03cd68f8384f994e88eb6f8c19076e5368a2a6fa Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sun, 30 Aug 2020 17:12:22 +0200 Subject: [PATCH 693/736] Fixes #104898 --- src/vs/base/common/arrays.ts | 11 ----------- src/vs/editor/common/modes/tokenizationRegistry.ts | 3 +-- .../platform/contextkey/browser/contextKeyService.ts | 3 +-- .../services/extensions/common/extensionsRegistry.ts | 3 +-- 4 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index d140e2ab635..48d9454ebf2 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -590,17 +590,6 @@ export function asArray(x: T | T[]): T[] { return Array.isArray(x) ? x : [x]; } -/** - * @deprecated Use `Array.from` or `[...iter]` - */ -export function toArray(iterable: IterableIterator): T[] { - const result: T[] = []; - for (let element of iterable) { - result.push(element); - } - return result; -} - export function getRandomElement(arr: T[]): T | undefined { return arr[Math.floor(Math.random() * arr.length)]; } diff --git a/src/vs/editor/common/modes/tokenizationRegistry.ts b/src/vs/editor/common/modes/tokenizationRegistry.ts index 3cb7a15ea65..c92c1296b19 100644 --- a/src/vs/editor/common/modes/tokenizationRegistry.ts +++ b/src/vs/editor/common/modes/tokenizationRegistry.ts @@ -7,7 +7,6 @@ import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ColorId, ITokenizationRegistry, ITokenizationSupport, ITokenizationSupportChangedEvent } from 'vs/editor/common/modes'; -import { toArray } from 'vs/base/common/arrays'; export class TokenizationRegistryImpl implements ITokenizationRegistry { @@ -82,7 +81,7 @@ export class TokenizationRegistryImpl implements ITokenizationRegistry { public setColorMap(colorMap: Color[]): void { this._colorMap = colorMap; this._onDidChange.fire({ - changedLanguages: toArray(this._map.keys()), + changedLanguages: Array.from(this._map.keys()), changedColorMap: true }); } diff --git a/src/vs/platform/contextkey/browser/contextKeyService.ts b/src/vs/platform/contextkey/browser/contextKeyService.ts index 89720995be2..4d9c1afbe55 100644 --- a/src/vs/platform/contextkey/browser/contextKeyService.ts +++ b/src/vs/platform/contextkey/browser/contextKeyService.ts @@ -9,7 +9,6 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContext, IContextKey, IContextKeyChangeEvent, IContextKeyService, IContextKeyServiceTarget, IReadableSet, SET_CONTEXT_COMMAND_ID, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; -import { toArray } from 'vs/base/common/arrays'; const KEYBINDING_CONTEXT_ATTR = 'data-keybinding-context'; @@ -102,7 +101,7 @@ class ConfigAwareContextValuesContainer extends Context { this._listener = this._configurationService.onDidChangeConfiguration(event => { if (event.source === ConfigurationTarget.DEFAULT) { // new setting, reset everything - const allKeys = toArray(this._values.keys()); + const allKeys = Array.from(this._values.keys()); this._values.clear(); emitter.fire(new ArrayContextKeyChangeEvent(allKeys)); } else { diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index 28b7f1e14b3..c454bf39efa 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -12,7 +12,6 @@ import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/c import { Registry } from 'vs/platform/registry/common/platform'; import { IMessage } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionIdentifier, IExtensionDescription, EXTENSION_CATEGORIES } from 'vs/platform/extensions/common/extensions'; -import { toArray } from 'vs/base/common/arrays'; const schemaRegistry = Registry.as(Extensions.JSONContribution); export type ExtensionKind = 'workspace' | 'ui' | undefined; @@ -446,7 +445,7 @@ export class ExtensionsRegistryImpl { } public getExtensionPoints(): ExtensionPoint[] { - return toArray(this._extensionPoints.values()); + return Array.from(this._extensionPoints.values()); } } From 33027a2331ea814e1e516ac9d9e5662ff82fa949 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sun, 30 Aug 2020 18:48:21 +0200 Subject: [PATCH 694/736] Log an error with the active extensions when the extension host crashes --- .../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 512b1c906ed..dd041f9cd9a 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -41,6 +41,7 @@ import { WebWorkerExtensionHost } from 'vs/workbench/services/extensions/browser import { IExtensionActivationHost as IWorkspaceContainsActivationHost, checkGlobFileExists, checkActivateWorkspaceContainsExtension } from 'vs/workbench/api/common/shared/workspaceContains'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { exists } from 'vs/base/node/pfs'; +import { ILogService } from 'vs/platform/log/common/log'; class DeltaExtensionsQueueItem { constructor( @@ -76,6 +77,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten @IRemoteExplorerService private readonly _remoteExplorerService: IRemoteExplorerService, @IExtensionGalleryService private readonly _extensionGalleryService: IExtensionGalleryService, @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, + @ILogService private readonly _logService: ILogService, ) { super( instantiationService, @@ -431,6 +433,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten } protected _onExtensionHostCrashed(extensionHost: ExtensionHostManager, code: number, signal: string | null): void { + const activatedExtensions = Array.from(this._extensionHostActiveExtensions.values()); super._onExtensionHostCrashed(extensionHost, code, signal); if (extensionHost.kind === ExtensionHostKind.LocalProcess) { @@ -451,6 +454,9 @@ export class ExtensionService extends AbstractExtensionService implements IExten return; } + const message = `Extension host terminated unexpectedly. The following extensions were running: ${activatedExtensions.map(id => id.value).join(', ')}`; + this._logService.error(message); + this._notificationService.prompt(Severity.Error, nls.localize('extensionService.crash', "Extension host terminated unexpectedly."), [{ label: nls.localize('devTools', "Open Developer Tools"), From b4eb8cfac3a39e0a0fc8b8dc8b29939318b92fa3 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sun, 30 Aug 2020 18:58:15 +0200 Subject: [PATCH 695/736] Fix event listening --- src/vs/editor/browser/widget/diffEditorWidget.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index b9da6aa8307..8be83dcae5f 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -514,8 +514,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE })); const isInDiffLeftEditorKey = contextKeyService.createKey('isInDiffLeftEditor', undefined); - this._register(Event.any(editor.onDidFocusEditorText, editor.onDidFocusEditorWidget)(() => isInDiffLeftEditorKey.set(true))); - this._register(Event.any(editor.onDidBlurEditorText, editor.onDidBlurEditorWidget)(() => isInDiffLeftEditorKey.set(false))); + this._register(editor.onDidFocusEditorWidget(() => isInDiffLeftEditorKey.set(true))); + this._register(editor.onDidBlurEditorWidget(() => isInDiffLeftEditorKey.set(false))); this._register(editor.onDidContentSizeChange(e => { const width = this.originalEditor.getContentWidth() + this.modifiedEditor.getContentWidth() + DiffEditorWidget.ONE_OVERVIEW_WIDTH; @@ -575,8 +575,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE })); const isInDiffRightEditorKey = contextKeyService.createKey('isInDiffRightEditor', undefined); - this._register(Event.any(editor.onDidFocusEditorText, editor.onDidFocusEditorWidget)(() => isInDiffRightEditorKey.set(true))); - this._register(Event.any(editor.onDidBlurEditorText, editor.onDidBlurEditorWidget)(() => isInDiffRightEditorKey.set(false))); + this._register(editor.onDidFocusEditorWidget(() => isInDiffRightEditorKey.set(true))); + this._register(editor.onDidBlurEditorWidget(() => isInDiffRightEditorKey.set(false))); this._register(editor.onDidContentSizeChange(e => { const width = this.originalEditor.getContentWidth() + this.modifiedEditor.getContentWidth() + DiffEditorWidget.ONE_OVERVIEW_WIDTH; From 5f31c7105df617a553a0ec45eae04f97b2e05199 Mon Sep 17 00:00:00 2001 From: IllusionMH Date: Wed, 1 Jul 2020 03:23:59 +0300 Subject: [PATCH 696/736] Add numeric values support for editor.fontWeight --- src/vs/editor/common/config/editorOptions.ts | 51 ++++++++++++++++---- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 30004656390..bf775e82235 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -1487,6 +1487,43 @@ class EditorFontSize extends SimpleEditorOption { //#endregion +//#region fontWeight + +class EditorFontWeight extends BaseEditorOption { + private static ENUM_VALUES = ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900']; + private static MINIMUM_VALUE = 1; + private static MAXIMUM_VALUE = 1000; + + constructor() { + super( + EditorOption.fontWeight, 'fontWeight', EDITOR_FONT_DEFAULTS.fontWeight, + { + anyOf: [ + { + type: 'number', + minimum: EditorFontWeight.MINIMUM_VALUE, + maximum: EditorFontWeight.MAXIMUM_VALUE + }, + { + enum: EditorFontWeight.ENUM_VALUES + } + ], + default: EDITOR_FONT_DEFAULTS.fontWeight, + description: nls.localize('fontWeight', "Controls the font weight.") + } + ); + } + + public validate(input: any): string { + if (typeof input === 'number') { + return EditorFontWeight.MINIMUM_VALUE <= input && input <= EditorFontWeight.MAXIMUM_VALUE ? String(input) : EDITOR_FONT_DEFAULTS.fontWeight; + } + return EditorStringEnumOption.stringSet(input, EDITOR_FONT_DEFAULTS.fontWeight, EditorFontWeight.ENUM_VALUES); + } +} + +//#endregion + //#region gotoLocation export type GoToLocationValues = 'peek' | 'gotoAndPeek' | 'goto'; @@ -1820,7 +1857,7 @@ export interface EditorLayoutInfoComputerEnv { readonly memory: ComputeOptionsMemory | null; readonly outerWidth: number; readonly outerHeight: number; - readonly isDominatedByLongLines: boolean + readonly isDominatedByLongLines: boolean; readonly lineHeight: number; readonly viewLineCount: number; readonly lineNumbersDigitCount: number; @@ -1835,7 +1872,7 @@ export interface EditorLayoutInfoComputerEnv { export interface IEditorLayoutComputerInput { readonly outerWidth: number; readonly outerHeight: number; - readonly isDominatedByLongLines: boolean + readonly isDominatedByLongLines: boolean; readonly lineHeight: number; readonly lineNumbersDigitCount: number; readonly typicalHalfwidthCharacterWidth: number; @@ -3098,7 +3135,7 @@ export interface ISuggestOptions { * Controls the visibility of the status bar at the bottom of the suggest widget. */ visible?: boolean; - } + }; } export type InternalSuggestOptions = Readonly>; @@ -3863,13 +3900,7 @@ export const EditorOptions = { fontInfo: register(new EditorFontInfo()), fontLigatures2: register(new EditorFontLigatures()), fontSize: register(new EditorFontSize()), - fontWeight: register(new EditorStringOption( - EditorOption.fontWeight, 'fontWeight', EDITOR_FONT_DEFAULTS.fontWeight, - { - enum: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], - description: nls.localize('fontWeight', "Controls the font weight.") - } - )), + fontWeight: register(new EditorFontWeight()), formatOnPaste: register(new EditorBooleanOption( EditorOption.formatOnPaste, 'formatOnPaste', false, { description: nls.localize('formatOnPaste', "Controls whether the editor should automatically format the pasted content. A formatter must be available and the formatter should be able to format a range in a document.") } From c89f8ff0f22be13d338c0d401192beadda2af98c Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sun, 30 Aug 2020 22:24:05 +0200 Subject: [PATCH 697/736] small style change --- src/vs/editor/common/modes/supports/tokenization.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/common/modes/supports/tokenization.ts b/src/vs/editor/common/modes/supports/tokenization.ts index 8cade505f3a..e701b60f0a9 100644 --- a/src/vs/editor/common/modes/supports/tokenization.ts +++ b/src/vs/editor/common/modes/supports/tokenization.ts @@ -397,13 +397,12 @@ export class ThemeTrieElement { export function generateTokensCSSForColorMap(colorMap: Color[]): string { let rules: string[] = []; - const editorParentClass = '.monaco-editor'; for (let i = 1, len = colorMap.length; i < len; i++) { let color = colorMap[i]; - rules[i] = `${editorParentClass} .mtk${i} { color: ${color}; }`; + rules[i] = `.monaco-editor .mtk${i} { color: ${color}; }`; } - rules.push(`${editorParentClass} .mtki { font-style: italic; }`); - rules.push(`${editorParentClass} .mtkb { font-weight: bold; }`); - rules.push(`${editorParentClass} .mtku { text-decoration: underline; text-underline-position: under; }`); + rules.push('.monaco-editor .mtki { font-style: italic; }'); + rules.push('.monaco-editor .mtkb { font-weight: bold; }'); + rules.push('.monaco-editor .mtku { text-decoration: underline; text-underline-position: under; }'); return rules.join('\n'); } From f66741dc2b4944e153719bb48bcaa9c565cc40f7 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Sun, 30 Aug 2020 23:09:37 -0400 Subject: [PATCH 698/736] Bumps version of github-browser --- product.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product.json b/product.json index 9e975d59c1b..7dcb54372e5 100644 --- a/product.json +++ b/product.json @@ -123,7 +123,7 @@ "webBuiltInExtensions": [ { "name": "ms-vscode.github-browser", - "version": "0.0.2", + "version": "0.0.3", "repo": "https://github.com/Microsoft/vscode-github-browser", "metadata": { "id": "c1bcff4b-4ecb-466e-b8f6-b02788b5fb5a", From ecdf4390345d2000582b00820c087228f610eef7 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 31 Aug 2020 08:20:44 +0200 Subject: [PATCH 699/736] :lipstick: use path label when presenting paths to user --- src/vs/platform/windows/electron-main/windowsMainService.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index d594c6d8cbb..1aec9526b10 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -40,6 +40,7 @@ import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs'; import { withNullAsUndefined } from 'vs/base/common/types'; import { isWindowsDriveLetter, toSlashes, parseLineAndColumnAware } from 'vs/base/common/extpath'; import { CharCode } from 'vs/base/common/charCode'; +import { getPathLabel } from 'vs/base/common/labels'; export interface IWindowState { workspace?: IWorkspaceIdentifier; @@ -880,11 +881,12 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic let message, detail; if (uri.scheme === Schemas.file) { message = localize('pathNotExistTitle', "Path does not exist"); - detail = localize('pathNotExistDetail', "The path '{0}' does not seem to exist anymore on disk.", uri.fsPath); + detail = localize('pathNotExistDetail', "The path '{0}' does not seem to exist anymore on disk.", getPathLabel(uri.fsPath, this.environmentService)); } else { message = localize('uriInvalidTitle', "URI can not be opened"); detail = localize('uriInvalidDetail', "The URI '{0}' is not valid and can not be opened.", uri.toString()); } + const options: MessageBoxOptions = { title: product.nameLong, type: 'info', From ae0dbb397dadbd2a504b7fd5990c7cc51077aa98 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 31 Aug 2020 08:54:35 +0200 Subject: [PATCH 700/736] :lipstick: desktop => native --- src/vs/base/browser/ui/list/listView.ts | 4 ++-- src/vs/base/common/stream.ts | 6 +++--- .../platform/environment/common/environment.ts | 4 ++-- src/vs/platform/files/common/files.ts | 2 +- src/vs/platform/menubar/electron-main/menubar.ts | 6 +++--- src/vs/platform/windows/common/windows.ts | 8 ++++---- .../files/browser/views/explorerViewer.ts | 16 ++++++++-------- .../files/browser/views/openEditorsView.ts | 4 ++-- .../terminalNativeContribution.ts | 6 +++--- .../workbench/electron-browser/desktop.main.ts | 4 ++-- src/vs/workbench/electron-browser/window.ts | 8 ++++---- .../extensionManagementServerService.ts | 4 ++-- .../remoteExtensionManagementService.ts | 2 +- ...esktopHostService.ts => nativeHostService.ts} | 4 ++-- .../services/log/electron-browser/logService.ts | 8 ++++---- src/vs/workbench/workbench.sandbox.main.ts | 2 +- 16 files changed, 44 insertions(+), 44 deletions(-) rename src/vs/workbench/services/host/electron-sandbox/{desktopHostService.ts => nativeHostService.ts} (96%) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 223eb6e437a..9821d6bcc6f 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -121,7 +121,7 @@ export class ExternalElementsDragAndDropData implements IDragAndDropData { } } -export class DesktopDragAndDropData implements IDragAndDropData { +export class NativeDragAndDropData implements IDragAndDropData { readonly types: any[]; readonly files: any[]; @@ -976,7 +976,7 @@ export class ListView implements ISpliceable, IDisposable { return false; } - this.currentDragData = new DesktopDragAndDropData(); + this.currentDragData = new NativeDragAndDropData(); } } diff --git a/src/vs/base/common/stream.ts b/src/vs/base/common/stream.ts index 7ed2befbe0d..260e13f02b3 100644 --- a/src/vs/base/common/stream.ts +++ b/src/vs/base/common/stream.ts @@ -33,7 +33,7 @@ export interface ReadableStreamEvents { /** * A interface that emulates the API shape of a node.js readable - * stream for use in desktop and web environments. + * stream for use in native and web environments. */ export interface ReadableStream extends ReadableStreamEvents { @@ -60,7 +60,7 @@ export interface ReadableStream extends ReadableStreamEvents { /** * A interface that emulates the API shape of a node.js readable - * for use in desktop and web environments. + * for use in native and web environments. */ export interface Readable { @@ -73,7 +73,7 @@ export interface Readable { /** * A interface that emulates the API shape of a node.js writeable - * stream for use in desktop and web environments. + * stream for use in native and web environments. */ export interface WriteableStream extends ReadableStream { diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 6c660f87ed8..6ff81a44fd2 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -23,7 +23,7 @@ export interface IEnvironmentService { // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // NOTE: DO NOT ADD ANY OTHER PROPERTY INTO THE COLLECTION HERE - // UNLESS THIS PROPERTY IS SUPPORTED BOTH IN WEB AND DESKTOP!!!! + // UNLESS THIS PROPERTY IS SUPPORTED BOTH IN WEB AND NATIVE!!!! // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! readonly _serviceBrand: undefined; @@ -70,6 +70,6 @@ export interface IEnvironmentService { // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // NOTE: DO NOT ADD ANY OTHER PROPERTY INTO THE COLLECTION HERE - // UNLESS THIS PROPERTY IS SUPPORTED BOTH IN WEB AND DESKTOP!!!! + // UNLESS THIS PROPERTY IS SUPPORTED BOTH IN WEB AND NATIVE!!!! // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! } diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index 39b57391424..2b046ac4a59 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -867,7 +867,7 @@ export function whenProviderRegistered(file: URI, fileService: IFileService): Pr } /** - * Desktop only: limits for memory sizes + * Native only: limits for memory sizes */ export const MIN_MAX_MEMORY_SIZE_MB = 2048; export const FALLBACK_MAX_MEMORY_SIZE_MB = 4096; diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index cfdb43bf474..bf78261135d 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { isMacintosh, language } from 'vs/base/common/platform'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { app, shell, Menu, MenuItem, BrowserWindow, MenuItemConstructorOptions, WebContents, Event, KeyboardEvent } from 'electron'; -import { getTitleBarStyle, IDesktopRunActionInWindowRequest, IDesktopRunKeybindingInWindowRequest, IWindowOpenable } from 'vs/platform/windows/common/windows'; +import { getTitleBarStyle, INativeRunActionInWindowRequest, INativeRunKeybindingInWindowRequest, IWindowOpenable } from 'vs/platform/windows/common/windows'; import { OpenContext } from 'vs/platform/windows/node/window'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -754,10 +754,10 @@ export class Menubar { } if (invocation.type === 'commandId') { - const runActionPayload: IDesktopRunActionInWindowRequest = { id: invocation.commandId, from: 'menu' }; + const runActionPayload: INativeRunActionInWindowRequest = { id: invocation.commandId, from: 'menu' }; activeWindow.sendWhenReady('vscode:runAction', runActionPayload); } else { - const runKeybindingPayload: IDesktopRunKeybindingInWindowRequest = { userSettingsLabel: invocation.userSettingsLabel }; + const runKeybindingPayload: INativeRunKeybindingInWindowRequest = { userSettingsLabel: invocation.userSettingsLabel }; activeWindow.sendWhenReady('vscode:runKeybinding', runKeybindingPayload); } } else { diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 77b74e12c61..5accb83f2ba 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -186,20 +186,20 @@ export interface IOpenFileRequest { } /** - * Additional context for the request on desktop only. + * Additional context for the request on native only. */ -export interface IDesktopOpenFileRequest extends IOpenFileRequest { +export interface INativeOpenFileRequest extends IOpenFileRequest { termProgram?: string; filesToWait?: IPathsToWaitForData; } -export interface IDesktopRunActionInWindowRequest { +export interface INativeRunActionInWindowRequest { id: string; from: 'menu' | 'touchbar' | 'mouse'; args?: any[]; } -export interface IDesktopRunKeybindingInWindowRequest { +export interface INativeRunKeybindingInWindowRequest { userSettingsLabel: string; } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 2d940ea198a..9700ce94e9a 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -34,7 +34,7 @@ import { fillResourceDataTransfers, CodeDataTransfers, extractResources, contain 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, ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; +import { NativeDragAndDropData, ExternalElementsDragAndDropData, ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { isMacintosh, isWeb } from 'vs/base/common/platform'; import { IDialogService, IConfirmation, getFileNamesMessage } from 'vs/platform/dialogs/common/dialogs'; import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; @@ -839,11 +839,11 @@ export class FileDragAndDrop implements ITreeDragAndDrop { private handleDragOver(data: IDragAndDropData, target: ExplorerItem | undefined, targetIndex: number | undefined, originalEvent: DragEvent): boolean | ITreeDragOverReaction { const isCopy = originalEvent && ((originalEvent.ctrlKey && !isMacintosh) || (originalEvent.altKey && isMacintosh)); - const fromDesktop = data instanceof DesktopDragAndDropData; - const effect = (fromDesktop || isCopy) ? ListDragOverEffect.Copy : ListDragOverEffect.Move; + const isNative = data instanceof NativeDragAndDropData; + const effect = (isNative || isCopy) ? ListDragOverEffect.Copy : ListDragOverEffect.Move; - // Desktop DND - if (fromDesktop) { + // Native DND + if (isNative) { if (!containsDragType(originalEvent, DataTransfers.FILES, CodeDataTransfers.FILES)) { return false; } @@ -979,7 +979,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { } // Desktop DND (Import file) - if (data instanceof DesktopDragAndDropData) { + if (data instanceof NativeDragAndDropData) { if (isWeb) { this.handleWebExternalDrop(data, target, originalEvent).then(undefined, e => this.notificationService.warn(e)); } else { @@ -992,7 +992,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { } } - private async handleWebExternalDrop(data: DesktopDragAndDropData, target: ExplorerItem, originalEvent: DragEvent): Promise { + private async handleWebExternalDrop(data: NativeDragAndDropData, target: ExplorerItem, originalEvent: DragEvent): Promise { const items = (originalEvent.dataTransfer as unknown as IWebkitDataTransfer).items; // Somehow the items thing is being modified at random, maybe as a security @@ -1205,7 +1205,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { }); } - private async handleExternalDrop(data: DesktopDragAndDropData, target: ExplorerItem, originalEvent: DragEvent): Promise { + private async handleExternalDrop(data: NativeDragAndDropData, target: ExplorerItem, originalEvent: DragEvent): Promise { // Check for dropped external files to be folders const droppedResources = extractResources(originalEvent, true); diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index 843ea8cd16f..e07fd997901 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -38,7 +38,7 @@ import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IDragAndDropData, DataTransfers } from 'vs/base/browser/dnd'; import { memoize } from 'vs/base/common/decorators'; -import { ElementsDragAndDropData, DesktopDragAndDropData } from 'vs/base/browser/ui/list/listView'; +import { ElementsDragAndDropData, NativeDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { URI } from 'vs/base/common/uri'; import { withUndefinedAsNull } from 'vs/base/common/types'; import { isWeb } from 'vs/base/common/platform'; @@ -667,7 +667,7 @@ class OpenEditorsDragAndDrop implements IListDragAndDrop this._onOpenFileRequest(request)); + ipcRenderer.on('vscode:openFiles', (_: unknown, request: INativeOpenFileRequest) => this._onOpenFileRequest(request)); this._register(electronService.onOSResume(() => this._onOsResume())); this._terminalService.setLinuxDistro(linuxDistro); @@ -53,7 +53,7 @@ export class TerminalNativeContribution extends Disposable implements IWorkbench activeTab.terminalInstances.forEach(instance => instance.forceRedraw()); } - private async _onOpenFileRequest(request: IDesktopOpenFileRequest): Promise { + private async _onOpenFileRequest(request: INativeOpenFileRequest): Promise { // if the request to open files is coming in from the integrated terminal (identified though // the termProgram variable) and we are instructed to wait for editors close, wait for the // marker file to get deleted and then focus back to the integrated terminal. diff --git a/src/vs/workbench/electron-browser/desktop.main.ts b/src/vs/workbench/electron-browser/desktop.main.ts index 06e8e0137c1..a0ed6c01538 100644 --- a/src/vs/workbench/electron-browser/desktop.main.ts +++ b/src/vs/workbench/electron-browser/desktop.main.ts @@ -48,7 +48,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import product from 'vs/platform/product/common/product'; import { NativeResourceIdentityService } from 'vs/platform/resource/node/resourceIdentityServiceImpl'; import { IResourceIdentityService } from 'vs/platform/resource/common/resourceIdentityService'; -import { DesktopLogService } from 'vs/workbench/services/log/electron-browser/logService'; +import { NativeLogService } from 'vs/workbench/services/log/electron-browser/logService'; import { IElectronService, ElectronService } from 'vs/platform/electron/electron-sandbox/electron'; class DesktopMain extends Disposable { @@ -183,7 +183,7 @@ class DesktopMain extends Disposable { serviceCollection.set(IProductService, productService); // Log - const logService = this._register(new DesktopLogService(this.configuration.windowId, mainProcessService, this.environmentService)); + const logService = this._register(new NativeLogService(this.configuration.windowId, mainProcessService, this.environmentService)); serviceCollection.set(ILogService, logService); // Remote diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 92cc77b5b03..1056c89d5b1 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -13,7 +13,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { toResource, IUntitledTextResourceEditorInput, SideBySideEditor, pathsToEditors } from 'vs/workbench/common/editor'; import { IEditorService, IResourceEditorInputType } from 'vs/workbench/services/editor/common/editorService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IOpenFileRequest, IWindowsConfiguration, getTitleBarStyle, IAddFoldersRequest, IDesktopRunActionInWindowRequest, IDesktopRunKeybindingInWindowRequest, IDesktopOpenFileRequest } from 'vs/platform/windows/common/windows'; +import { IOpenFileRequest, IWindowsConfiguration, getTitleBarStyle, IAddFoldersRequest, INativeRunActionInWindowRequest, INativeRunKeybindingInWindowRequest, INativeOpenFileRequest } from 'vs/platform/windows/common/windows'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { applyZoom } from 'vs/platform/windows/electron-sandbox/window'; @@ -128,7 +128,7 @@ export class NativeWindow extends Disposable { }); // Support runAction event - ipcRenderer.on('vscode:runAction', async (event: unknown, request: IDesktopRunActionInWindowRequest) => { + ipcRenderer.on('vscode:runAction', async (event: unknown, request: INativeRunActionInWindowRequest) => { const args: unknown[] = request.args || []; // If we run an action from the touchbar, we fill in the currently active resource @@ -159,7 +159,7 @@ export class NativeWindow extends Disposable { }); // Support runKeybinding event - ipcRenderer.on('vscode:runKeybinding', (event: unknown, request: IDesktopRunKeybindingInWindowRequest) => { + ipcRenderer.on('vscode:runKeybinding', (event: unknown, request: INativeRunKeybindingInWindowRequest) => { if (document.activeElement) { this.keybindingService.dispatchByUserSettingsLabel(request.userSettingsLabel, document.activeElement); } @@ -547,7 +547,7 @@ export class NativeWindow extends Disposable { this.workspaceEditingService.addFolders(foldersToAdd); } - private async onOpenFiles(request: IDesktopOpenFileRequest): Promise { + private async onOpenFiles(request: INativeOpenFileRequest): Promise { const inputs: IResourceEditorInputType[] = []; const diffMode = !!(request.filesToDiff && (request.filesToDiff.length === 2)); diff --git a/src/vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService.ts b/src/vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService.ts index 7aabfa3b995..98538abceb8 100644 --- a/src/vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService.ts @@ -12,7 +12,7 @@ import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { DesktopRemoteExtensionManagementService } from 'vs/workbench/services/extensionManagement/electron-browser/remoteExtensionManagementService'; +import { NativeRemoteExtensionManagementService } from 'vs/workbench/services/extensionManagement/electron-browser/remoteExtensionManagementService'; import { ILabelService } from 'vs/platform/label/common/label'; import { IExtension } from 'vs/platform/extensions/common/extensions'; import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; @@ -43,7 +43,7 @@ export class ExtensionManagementServerService implements IExtensionManagementSer this._localExtensionManagementServer = { extensionManagementService: localExtensionManagementService, id: 'local', label: localize('local', "Local") }; const remoteAgentConnection = remoteAgentService.getConnection(); if (remoteAgentConnection) { - const extensionManagementService = new DesktopRemoteExtensionManagementService(remoteAgentConnection.getChannel('extensions'), this.localExtensionManagementServer, logService, galleryService, configurationService, productService); + const extensionManagementService = new NativeRemoteExtensionManagementService(remoteAgentConnection.getChannel('extensions'), this.localExtensionManagementServer, logService, galleryService, configurationService, productService); this.remoteExtensionManagementServer = { id: 'remote', extensionManagementService, diff --git a/src/vs/workbench/services/extensionManagement/electron-browser/remoteExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/electron-browser/remoteExtensionManagementService.ts index 749f01c3bc9..49d6ec9fbdf 100644 --- a/src/vs/workbench/services/extensionManagement/electron-browser/remoteExtensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-browser/remoteExtensionManagementService.ts @@ -22,7 +22,7 @@ import { joinPath } from 'vs/base/common/resources'; import { WebRemoteExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/remoteExtensionManagementService'; import { IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -export class DesktopRemoteExtensionManagementService extends WebRemoteExtensionManagementService implements IExtensionManagementService { +export class NativeRemoteExtensionManagementService extends WebRemoteExtensionManagementService implements IExtensionManagementService { private readonly localExtensionManagementService: IExtensionManagementService; diff --git a/src/vs/workbench/services/host/electron-sandbox/desktopHostService.ts b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts similarity index 96% rename from src/vs/workbench/services/host/electron-sandbox/desktopHostService.ts rename to src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts index ac98ee8f1fa..86aa6287b4a 100644 --- a/src/vs/workbench/services/host/electron-sandbox/desktopHostService.ts +++ b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts @@ -12,7 +12,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { IWindowOpenable, IOpenWindowOptions, isFolderToOpen, isWorkspaceToOpen, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; import { Disposable } from 'vs/base/common/lifecycle'; -export class DesktopHostService extends Disposable implements IHostService { +export class NativeHostService extends Disposable implements IHostService { declare readonly _serviceBrand: undefined; @@ -95,4 +95,4 @@ export class DesktopHostService extends Disposable implements IHostService { } } -registerSingleton(IHostService, DesktopHostService, true); +registerSingleton(IHostService, NativeHostService, true); diff --git a/src/vs/workbench/services/log/electron-browser/logService.ts b/src/vs/workbench/services/log/electron-browser/logService.ts index caa1bd713c0..6457c582eb4 100644 --- a/src/vs/workbench/services/log/electron-browser/logService.ts +++ b/src/vs/workbench/services/log/electron-browser/logService.ts @@ -14,7 +14,7 @@ import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions } f import { Registry } from 'vs/platform/registry/common/platform'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -export class DesktopLogService extends DelegatedLogService { +export class NativeLogService extends DelegatedLogService { private readonly bufferSpdLogService: BufferLogService | undefined; private readonly windowId: number; @@ -62,11 +62,11 @@ export class DesktopLogService extends DelegatedLogService { } } -class DesktopLogServiceInitContribution implements IWorkbenchContribution { +class NativeLogServiceInitContribution implements IWorkbenchContribution { constructor(@ILogService logService: ILogService) { - if (logService instanceof DesktopLogService) { + if (logService instanceof NativeLogService) { logService.init(); } } } -Registry.as(Extensions.Workbench).registerWorkbenchContribution(DesktopLogServiceInitContribution, LifecyclePhase.Restored); +Registry.as(Extensions.Workbench).registerWorkbenchContribution(NativeLogServiceInitContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/workbench.sandbox.main.ts b/src/vs/workbench/workbench.sandbox.main.ts index 4ab428b3565..038fcb6f660 100644 --- a/src/vs/workbench/workbench.sandbox.main.ts +++ b/src/vs/workbench/workbench.sandbox.main.ts @@ -28,7 +28,7 @@ import 'vs/workbench/services/update/electron-sandbox/updateService'; import 'vs/workbench/services/url/electron-sandbox/urlService'; import 'vs/workbench/services/lifecycle/electron-sandbox/lifecycleService'; import 'vs/workbench/services/title/electron-sandbox/titleService'; -import 'vs/workbench/services/host/electron-sandbox/desktopHostService'; +import 'vs/workbench/services/host/electron-sandbox/nativeHostService'; import 'vs/workbench/services/request/electron-sandbox/requestService'; import 'vs/workbench/services/extensionResourceLoader/electron-sandbox/extensionResourceLoaderService'; import 'vs/workbench/services/clipboard/electron-sandbox/clipboardService'; From bb6a1431d7b48d20cec0aa142ad6eb46bdfb440b Mon Sep 17 00:00:00 2001 From: danecreekphotography Date: Mon, 31 Aug 2020 00:09:44 -0700 Subject: [PATCH 701/736] 105160: Support configuring keyboard overlay timeout + mouse indicator color/size (#105219) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 陈大大哦了 Co-authored-by: Ladislau Szomoru --- .../browser/actions/developerActions.ts | 74 +++++++++++++++++-- .../browser/actions/media/actions.css | 8 +- 2 files changed, 69 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/browser/actions/developerActions.ts b/src/vs/workbench/browser/actions/developerActions.ts index d769e61957c..4a9da40d32d 100644 --- a/src/vs/workbench/browser/actions/developerActions.ts +++ b/src/vs/workbench/browser/actions/developerActions.ts @@ -123,14 +123,37 @@ class ToggleScreencastModeAction extends Action2 { const onMouseUp = domEvent(container, 'mouseup', true); const onMouseMove = domEvent(container, 'mousemove', true); + let mouseIndicatorColor: string; + const updateMouseIndicatorColor = () => { + mouseIndicatorColor = configurationService.getValue('screencastMode.mouseIndicatorColor'); + + let style = new Option().style; + style.color = mouseIndicatorColor; + if (mouseIndicatorColor === '' || !style.color) { + mouseIndicatorColor = 'red'; + } + }; + + let mouseIndicatorSize: number; + const updateMouseIndicatorSize = () => { + mouseIndicatorSize = clamp(configurationService.getValue('screencastMode.mouseIndicatorSize') || 20, 20, 100); + }; + + updateMouseIndicatorColor(); + updateMouseIndicatorSize(); + disposables.add(onMouseDown(e => { - mouseMarker.style.top = `${e.clientY - 10}px`; - mouseMarker.style.left = `${e.clientX - 10}px`; + mouseMarker.style.height = `${mouseIndicatorSize}px`; + mouseMarker.style.width = `${mouseIndicatorSize}px`; + mouseMarker.style.borderRadius = '50%'; + mouseMarker.style.borderColor = mouseIndicatorColor; + mouseMarker.style.top = `${e.clientY - mouseIndicatorSize / 2}px`; + mouseMarker.style.left = `${e.clientX - mouseIndicatorSize / 2}px`; mouseMarker.style.display = 'block'; const mouseMoveListener = onMouseMove(e => { - mouseMarker.style.top = `${e.clientY - 10}px`; - mouseMarker.style.left = `${e.clientX - 10}px`; + mouseMarker.style.top = `${e.clientY - mouseIndicatorSize / 2}px`; + mouseMarker.style.left = `${e.clientX - mouseIndicatorSize / 2}px`; }); Event.once(onMouseUp)(() => { @@ -150,8 +173,14 @@ class ToggleScreencastModeAction extends Action2 { keyboardMarker.style.bottom = `${clamp(configurationService.getValue('screencastMode.verticalOffset') || 0, 0, 90)}%`; }; + let keyboardMarkerTimeout: number; + const updateKeyboardMarkerTimeout = () => { + keyboardMarkerTimeout = clamp(configurationService.getValue('screencastMode.keyboardOverlayTimeout') || 800, 500, 5000); + }; + updateKeyboardFontSize(); updateKeyboardMarker(); + updateKeyboardMarkerTimeout(); disposables.add(configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('screencastMode.verticalOffset')) { @@ -161,6 +190,18 @@ class ToggleScreencastModeAction extends Action2 { if (e.affectsConfiguration('screencastMode.fontSize')) { updateKeyboardFontSize(); } + + if (e.affectsConfiguration('screencastMode.keyboardOverlayTimeout')) { + updateKeyboardMarkerTimeout(); + } + + if (e.affectsConfiguration('screencastMode.mouseIndicatorColor')) { + updateMouseIndicatorColor(); + } + + if (e.affectsConfiguration('screencastMode.mouseIndicatorSize')) { + updateMouseIndicatorSize(); + } })); const onKeyDown = domEvent(window, 'keydown', true); @@ -190,7 +231,7 @@ class ToggleScreencastModeAction extends Action2 { append(keyboardMarker, key); } - const promise = timeout(800); + const promise = timeout(keyboardMarkerTimeout); keyboardTimeout = toDisposable(() => promise.cancel()); promise.then(() => { @@ -276,8 +317,27 @@ configurationRegistry.registerConfiguration({ }, 'screencastMode.onlyKeyboardShortcuts': { type: 'boolean', - description: nls.localize('screencastMode.onlyKeyboardShortcuts', "Only show keyboard shortcuts in Screencast Mode."), + description: nls.localize('screencastMode.onlyKeyboardShortcuts', "Only show keyboard shortcuts in screencast mode."), default: false - } + }, + 'screencastMode.keyboardOverlayTimeout': { + type: 'number', + default: 800, + minimum: 500, + maximum: 5000, + description: nls.localize('screencastMode.keyboardOverlayTimeout', "Controls how long (in milliseconds) the keyboard overlay is shown in screencast mode.") + }, + 'screencastMode.mouseIndicatorColor': { + type: 'string', + default: 'red', + description: nls.localize('screencastMode.mouseIndicatorColor', "Controls the color (string or Hex) of the mouse indicator in screencast mode.") + }, + 'screencastMode.mouseIndicatorSize': { + type: 'number', + default: 20, + minimum: 20, + maximum: 100, + description: nls.localize('screencastMode.mouseIndicatorSize', "Controls the size (in pixels) of the mouse indicator in screencast mode.") + }, } }); diff --git a/src/vs/workbench/browser/actions/media/actions.css b/src/vs/workbench/browser/actions/media/actions.css index e87f0156206..2397d2d48f6 100644 --- a/src/vs/workbench/browser/actions/media/actions.css +++ b/src/vs/workbench/browser/actions/media/actions.css @@ -9,12 +9,8 @@ .monaco-workbench .screencast-mouse { position: absolute; - border: 2px solid red; - border-radius: 20px; - width: 20px; - height: 20px; - top: 0; - left: 0; + border-width: 2px; + border-style: solid; z-index: 100000; content: ' '; pointer-events: none; From 639b39927d50afc986851c07ca1b8de2eef1f9f4 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 31 Aug 2020 09:55:38 +0200 Subject: [PATCH 702/736] remove asAbsolutePath-confusion for web-context, https://github.com/microsoft/vscode/issues/105277 --- src/vs/workbench/api/common/extHostExtensionService.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 6d9e1a525e0..0bb5188614b 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -5,7 +5,6 @@ import * as nls from 'vs/nls'; import * as path from 'vs/base/common/path'; -import * as platform from 'vs/base/common/platform'; import { originalFSPath, joinPath } from 'vs/base/common/resources'; import { Barrier, timeout } from 'vs/base/common/async'; import { dispose, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; @@ -385,14 +384,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme subscriptions: [], get extensionUri() { return extensionDescription.extensionLocation; }, get extensionPath() { return extensionDescription.extensionLocation.fsPath; }, - asAbsolutePath(relativePath: string) { - if (platform.isWeb) { - // web worker - return URI.joinPath(extensionDescription.extensionLocation, relativePath).toString(); - } else { - return path.join(extensionDescription.extensionLocation.fsPath, relativePath); - } - }, + asAbsolutePath(relativePath: string) { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); }, get storagePath() { return that._storagePath.workspaceValue(extensionDescription)?.fsPath; }, get globalStoragePath() { return that._storagePath.globalValue(extensionDescription).fsPath; }, get logPath() { return path.join(that._initData.logsLocation.fsPath, extensionDescription.identifier.value); }, From 57e9e1ad6076f1fa8b7216dfd95879ec714ce6aa Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 31 Aug 2020 10:01:01 +0200 Subject: [PATCH 703/736] update docs for asAbsolutePath, https://github.com/microsoft/vscode/issues/105277 --- src/vs/vscode.d.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 9a09ec73ed2..07f2be17b28 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -5626,6 +5626,9 @@ declare module 'vscode' { /** * Get the absolute path of a resource contained in the extension. * + * *Note* that an absolute uri can be constructed via [`Uri.joinPath`](#Uri.joinPath) and + * [`extensionUri`](#ExtensionContent.extensionUri), e.g. `vscode.Uri.joinPath(context.extensionUri, relativePath);` + * * @param relativePath A relative path to a resource contained in the extension. * @return The absolute path of the resource. */ From 9b50b84c4cdcadfea05c05a5adfbf5cf24a3bfd5 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 31 Aug 2020 10:58:16 +0200 Subject: [PATCH 704/736] use import-type for modules we load async (#105594) --- src/vs/code/node/cli.ts | 2 +- .../terminal/browser/addons/commandTrackerAddon.ts | 2 +- .../terminal/browser/addons/navigationModeAddon.ts | 2 +- .../terminal/browser/links/terminalBaseLinkProvider.ts | 2 +- .../browser/links/terminalExternalLinkProviderAdapter.ts | 2 +- .../contrib/terminal/browser/links/terminalLink.ts | 2 +- .../contrib/terminal/browser/links/terminalLinkHelpers.ts | 2 +- .../contrib/terminal/browser/links/terminalLinkManager.ts | 2 +- .../browser/links/terminalProtocolLinkProvider.ts | 2 +- .../browser/links/terminalValidatedLocalLinkProvider.ts | 2 +- .../terminal/browser/links/terminalWordLinkProvider.ts | 2 +- src/vs/workbench/contrib/terminal/browser/terminal.ts | 8 ++++---- .../contrib/terminal/browser/terminalInstance.ts | 6 +++--- .../contrib/terminal/browser/terminalInstanceService.ts | 8 ++++---- .../terminal/browser/widgets/terminalHoverWidget.ts | 2 +- .../terminal/electron-browser/terminalInstanceService.ts | 8 ++++---- .../terminal/electron-browser/windowsShellHelper.ts | 4 ++-- .../extensions/electron-browser/extensionHostProfiler.ts | 2 +- .../services/textMate/browser/abstractTextMateService.ts | 2 +- .../services/textMate/common/TMGrammarFactory.ts | 2 +- .../services/textMate/electron-browser/textMateService.ts | 2 +- .../services/textMate/electron-browser/textMateWorker.ts | 2 +- 22 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 83a9bc61e54..69f2f37b3df 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -14,7 +14,7 @@ import * as paths from 'vs/base/common/path'; import { whenDeleted, writeFileSync } from 'vs/base/node/pfs'; import { findFreePort, randomPort } from 'vs/base/node/ports'; import { isWindows, isLinux } from 'vs/base/common/platform'; -import { ProfilingSession, Target } from 'v8-inspect-profiler'; +import type { ProfilingSession, Target } from 'v8-inspect-profiler'; import { isString } from 'vs/base/common/types'; import { hasStdinWithoutTty, stdinDataListener, getStdinFilePath, readFromStdin } from 'vs/platform/environment/node/stdin'; diff --git a/src/vs/workbench/contrib/terminal/browser/addons/commandTrackerAddon.ts b/src/vs/workbench/contrib/terminal/browser/addons/commandTrackerAddon.ts index c81d1e9cc82..01c90349d9e 100644 --- a/src/vs/workbench/contrib/terminal/browser/addons/commandTrackerAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/addons/commandTrackerAddon.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Terminal, IMarker, ITerminalAddon } from 'xterm'; +import type { Terminal, IMarker, ITerminalAddon } from 'xterm'; import { ICommandTracker } from 'vs/workbench/contrib/terminal/common/terminal'; /** diff --git a/src/vs/workbench/contrib/terminal/browser/addons/navigationModeAddon.ts b/src/vs/workbench/contrib/terminal/browser/addons/navigationModeAddon.ts index baba436e968..8487459af57 100644 --- a/src/vs/workbench/contrib/terminal/browser/addons/navigationModeAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/addons/navigationModeAddon.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { Terminal, ITerminalAddon } from 'xterm'; +import type { Terminal, ITerminalAddon } from 'xterm'; import { addDisposableListener } from 'vs/base/browser/dom'; import { INavigationMode } from 'vs/workbench/contrib/terminal/common/terminal'; diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalBaseLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalBaseLinkProvider.ts index 38301d0e32c..3adccc7f5c2 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalBaseLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalBaseLinkProvider.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ILinkProvider, ILink } from 'xterm'; +import type { ILinkProvider, ILink } from 'xterm'; import { TerminalLink } from 'vs/workbench/contrib/terminal/browser/links/terminalLink'; export abstract class TerminalBaseLinkProvider implements ILinkProvider { diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalExternalLinkProviderAdapter.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalExternalLinkProviderAdapter.ts index 40478f7f7f9..fce440f2fd6 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalExternalLinkProviderAdapter.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalExternalLinkProviderAdapter.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Terminal, IViewportRange, IBufferLine } from 'xterm'; +import type { Terminal, IViewportRange, IBufferLine } from 'xterm'; import { getXtermLineContent, convertLinkRangeToBuffer } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers'; import { TerminalLink } from 'vs/workbench/contrib/terminal/browser/links/terminalLink'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLink.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLink.ts index d0d122fae55..a5b5b340215 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalLink.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLink.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IViewportRange, IBufferRange, ILink, ILinkDecorations } from 'xterm'; +import type { IViewportRange, IBufferRange, ILink, ILinkDecorations } from 'xterm'; import { DisposableStore } from 'vs/base/common/lifecycle'; import * as dom from 'vs/base/browser/dom'; import { RunOnceScheduler } from 'vs/base/common/async'; diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers.ts index e39db0a24d6..f2b85d1e3b5 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IViewportRange, IBufferRange, IBufferLine, IBuffer, IBufferCellPosition } from 'xterm'; +import type { IViewportRange, IBufferRange, IBufferLine, IBuffer, IBufferCellPosition } from 'xterm'; import { IRange } from 'vs/editor/common/core/range'; export function convertLinkRangeToBuffer(lines: IBufferLine[], bufferWidth: number, range: IRange, startLine: number) { diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts index 00d3c14b542..25ecbac5a6f 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts @@ -13,7 +13,7 @@ import { ITerminalProcessManager, ITerminalConfiguration, TERMINAL_CONFIG_SECTIO import { ITextEditorSelection } from 'vs/platform/editor/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IFileService } from 'vs/platform/files/common/files'; -import { Terminal, IViewportRange, ILinkProvider } from 'xterm'; +import type { Terminal, IViewportRange, ILinkProvider } from 'xterm'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { posix, win32 } from 'vs/base/common/path'; import { ITerminalExternalLinkProvider, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalProtocolLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalProtocolLinkProvider.ts index 2e47dfe72ef..615857b82ac 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalProtocolLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalProtocolLinkProvider.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Terminal, IViewportRange, IBufferLine } from 'xterm'; +import type { Terminal, IViewportRange, IBufferLine } from 'xterm'; import { ILinkComputerTarget, LinkComputer } from 'vs/editor/common/modes/linkComputer'; import { getXtermLineContent, convertLinkRangeToBuffer } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers'; import { TerminalLink, OPEN_FILE_LABEL } from 'vs/workbench/contrib/terminal/browser/links/terminalLink'; diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts index 287afdf9e1a..ada3c0be88d 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Terminal, IViewportRange, IBufferLine } from 'xterm'; +import type { Terminal, IViewportRange, IBufferLine } from 'xterm'; import { getXtermLineContent, convertLinkRangeToBuffer } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers'; import { OperatingSystem } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts index 5b501192c49..3947f9a1556 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Terminal, IViewportRange } from 'xterm'; +import type { Terminal, IViewportRange } from 'xterm'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITerminalConfiguration, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalLink } from 'vs/workbench/contrib/terminal/browser/links/terminalLink'; diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index a7db7423cfa..84631cdac69 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Terminal as XTermTerminal } from 'xterm'; -import { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; -import { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11'; -import { WebglAddon as XTermWebglAddon } from 'xterm-addon-webgl'; +import type { Terminal as XTermTerminal } from 'xterm'; +import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; +import type { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11'; +import type { WebglAddon as XTermWebglAddon } from 'xterm-addon-webgl'; import { IWindowsShellHelper, ITerminalConfigHelper, ITerminalChildProcess, IShellLaunchConfig, IDefaultShellAndArgsRequest, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, ITerminalProcessExtHostProxy, ICommandTracker, INavigationMode, TitleEventSource, ITerminalDimensions, ITerminalLaunchError, ITerminalNativeWindowsDelegate, LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProcessEnvironment, Platform } from 'vs/base/common/platform'; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 7739e56ef72..f80982e132a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -32,9 +32,9 @@ import { TerminalLinkManager } from 'vs/workbench/contrib/terminal/browser/links import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { ITerminalInstanceService, ITerminalInstance, TerminalShellType, WindowsShellType, ITerminalExternalLinkProvider } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/browser/terminalProcessManager'; -import { Terminal as XTermTerminal, IBuffer, ITerminalAddon } from 'xterm'; -import { SearchAddon, ISearchOptions } from 'xterm-addon-search'; -import { Unicode11Addon } from 'xterm-addon-unicode11'; +import type { Terminal as XTermTerminal, IBuffer, ITerminalAddon } from 'xterm'; +import type { SearchAddon, ISearchOptions } from 'xterm-addon-search'; +import type { Unicode11Addon } from 'xterm-addon-unicode11'; import { CommandTrackerAddon } from 'vs/workbench/contrib/terminal/browser/addons/commandTrackerAddon'; import { NavigationModeAddon } from 'vs/workbench/contrib/terminal/browser/addons/navigationModeAddon'; import { XTermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private'; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts index 52b03f24ccb..daefe15f7ef 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts @@ -5,10 +5,10 @@ import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IWindowsShellHelper, ITerminalChildProcess, IDefaultShellAndArgsRequest } from 'vs/workbench/contrib/terminal/common/terminal'; -import { Terminal as XTermTerminal } from 'xterm'; -import { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; -import { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11'; -import { WebglAddon as XTermWebglAddon } from 'xterm-addon-webgl'; +import type { Terminal as XTermTerminal } from 'xterm'; +import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; +import type { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11'; +import type { WebglAddon as XTermWebglAddon } from 'xterm-addon-webgl'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { Emitter, Event } from 'vs/base/common/event'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; diff --git a/src/vs/workbench/contrib/terminal/browser/widgets/terminalHoverWidget.ts b/src/vs/workbench/contrib/terminal/browser/widgets/terminalHoverWidget.ts index 121573ea0f5..7633a258cbe 100644 --- a/src/vs/workbench/contrib/terminal/browser/widgets/terminalHoverWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/widgets/terminalHoverWidget.ts @@ -8,7 +8,7 @@ import { IMarkdownString } from 'vs/base/common/htmlContent'; import { Widget } from 'vs/base/browser/ui/widget'; import { ITerminalWidget } from 'vs/workbench/contrib/terminal/browser/widgets/widgets'; import * as dom from 'vs/base/browser/dom'; -import { IViewportRange } from 'xterm'; +import type { IViewportRange } from 'xterm'; import { IHoverTarget, IHoverService } from 'vs/workbench/services/hover/browser/hover'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { editorHoverHighlight } from 'vs/platform/theme/common/colorRegistry'; diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts index 2dbb6a0f2be..40dcda2cd6f 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts @@ -10,10 +10,10 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IProcessEnvironment, platform, Platform } from 'vs/base/common/platform'; import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess'; import { getSystemShell } from 'vs/workbench/contrib/terminal/node/terminal'; -import { Terminal as XTermTerminal } from 'xterm'; -import { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; -import { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11'; -import { WebglAddon as XTermWebglAddon } from 'xterm-addon-webgl'; +import type { Terminal as XTermTerminal } from 'xterm'; +import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; +import type { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11'; +import type { WebglAddon as XTermWebglAddon } from 'xterm-addon-webgl'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { getDefaultShell, getDefaultShellArgs } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { StorageScope, IStorageService } from 'vs/platform/storage/common/storage'; diff --git a/src/vs/workbench/contrib/terminal/electron-browser/windowsShellHelper.ts b/src/vs/workbench/contrib/terminal/electron-browser/windowsShellHelper.ts index 37267ec9f43..495e9fccc90 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/windowsShellHelper.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/windowsShellHelper.ts @@ -6,8 +6,8 @@ import * as platform from 'vs/base/common/platform'; import { Emitter, Event } from 'vs/base/common/event'; import { IWindowsShellHelper } from 'vs/workbench/contrib/terminal/common/terminal'; -import { Terminal as XTermTerminal } from 'xterm'; -import * as WindowsProcessTreeType from 'windows-process-tree'; +import type { Terminal as XTermTerminal } from 'xterm'; +import type * as WindowsProcessTreeType from 'windows-process-tree'; import { Disposable } from 'vs/base/common/lifecycle'; import { timeout } from 'vs/base/common/async'; diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts index 914178135b7..342ec2dc402 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Profile, ProfileNode } from 'v8-inspect-profiler'; +import type { Profile, ProfileNode } from 'v8-inspect-profiler'; import { TernarySearchTree } from 'vs/base/common/map'; import { realpathSync } from 'vs/base/node/extpath'; import { IExtensionHostProfile, IExtensionService, ProfileSegmentId, ProfileSession } from 'vs/workbench/services/extensions/common/extensions'; diff --git a/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts b/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts index d4a98b3fade..6567c19b739 100644 --- a/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts +++ b/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts @@ -24,7 +24,7 @@ import { ExtensionMessageCollector } from 'vs/workbench/services/extensions/comm import { ITMSyntaxExtensionPoint, grammarsExtPoint } from 'vs/workbench/services/textMate/common/TMGrammars'; import { ITextMateService } from 'vs/workbench/services/textMate/common/textMateService'; import { ITextMateThemingRule, IWorkbenchThemeService, IWorkbenchColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; -import { IGrammar, StackElement, IOnigLib, IRawTheme } from 'vscode-textmate'; +import type { IGrammar, StackElement, IOnigLib, IRawTheme } from 'vscode-textmate'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IValidGrammarDefinition, IValidEmbeddedLanguagesMap, IValidTokenTypeMap } from 'vs/workbench/services/textMate/common/TMScopeRegistry'; diff --git a/src/vs/workbench/services/textMate/common/TMGrammarFactory.ts b/src/vs/workbench/services/textMate/common/TMGrammarFactory.ts index 187d10b3ba8..955eed45850 100644 --- a/src/vs/workbench/services/textMate/common/TMGrammarFactory.ts +++ b/src/vs/workbench/services/textMate/common/TMGrammarFactory.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import { LanguageId } from 'vs/editor/common/modes'; -import { IGrammar, Registry, StackElement, IRawTheme, IOnigLib } from 'vscode-textmate'; +import type { IGrammar, Registry, StackElement, IRawTheme, IOnigLib } from 'vscode-textmate'; import { Disposable } from 'vs/base/common/lifecycle'; import { TMScopeRegistry, IValidGrammarDefinition, IValidEmbeddedLanguagesMap } from 'vs/workbench/services/textMate/common/TMScopeRegistry'; diff --git a/src/vs/workbench/services/textMate/electron-browser/textMateService.ts b/src/vs/workbench/services/textMate/electron-browser/textMateService.ts index 87341b9950f..1b01a674aa1 100644 --- a/src/vs/workbench/services/textMate/electron-browser/textMateService.ts +++ b/src/vs/workbench/services/textMate/electron-browser/textMateService.ts @@ -13,7 +13,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { createWebWorker, MonacoWebWorker } from 'vs/editor/common/services/webWorker'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { IRawTheme } from 'vscode-textmate'; +import type { IRawTheme } from 'vscode-textmate'; import { IValidGrammarDefinition } from 'vs/workbench/services/textMate/common/TMScopeRegistry'; import { TextMateWorker } from 'vs/workbench/services/textMate/electron-browser/textMateWorker'; import { ITextModel } from 'vs/editor/common/model'; diff --git a/src/vs/workbench/services/textMate/electron-browser/textMateWorker.ts b/src/vs/workbench/services/textMate/electron-browser/textMateWorker.ts index de904376230..8a3f0ffb48a 100644 --- a/src/vs/workbench/services/textMate/electron-browser/textMateWorker.ts +++ b/src/vs/workbench/services/textMate/electron-browser/textMateWorker.ts @@ -11,7 +11,7 @@ import { TMGrammarFactory, ICreateGrammarResult } from 'vs/workbench/services/te import { IModelChangedEvent, MirrorTextModel } from 'vs/editor/common/model/mirrorTextModel'; import { TextMateWorkerHost } from 'vs/workbench/services/textMate/electron-browser/textMateService'; import { TokenizationStateStore } from 'vs/editor/common/model/textModelTokens'; -import { IGrammar, StackElement, IRawTheme, IOnigLib } from 'vscode-textmate'; +import type { IGrammar, StackElement, IRawTheme, IOnigLib } from 'vscode-textmate'; import { MultilineTokensBuilder, countEOL } from 'vs/editor/common/model/tokensStore'; import { LineTokens } from 'vs/editor/common/core/lineTokens'; From d177a392d996eb6bff4a222c8f252d1958fe01a0 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 31 Aug 2020 11:46:39 +0200 Subject: [PATCH 705/736] fixes #105677 --- .../workbench/contrib/debug/browser/media/repl.css | 12 +++--------- src/vs/workbench/contrib/debug/browser/replFilter.ts | 3 +-- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/media/repl.css b/src/vs/workbench/contrib/debug/browser/media/repl.css index adfebd7645e..2eaa06164d0 100644 --- a/src/vs/workbench/contrib/debug/browser/media/repl.css +++ b/src/vs/workbench/contrib/debug/browser/media/repl.css @@ -88,7 +88,7 @@ .monaco-workbench .repl .repl-tree .output.expression .code-italic { font-style: italic; } .monaco-workbench .repl .repl-tree .output.expression .code-underline { text-decoration: underline; } -.monaco-action-bar .action-item.panel-action-tree-filter-container { +.monaco-action-bar .action-item.repl-panel-filter-container { cursor: default; display: flex; } @@ -114,13 +114,7 @@ height: 25px; } -.panel > .title .monaco-action-bar .action-item.panel-action-tree-filter-container { - max-width: 400px; - min-width: 300px; +.panel > .title .monaco-action-bar .action-item.repl-panel-filter-container { + min-width: 200px; margin-right: 10px; } - -.monaco-action-bar .action-item.panel-action-tree-filter-container, -.panel > .title .monaco-action-bar .action-item.panel-action-tree-filter-container.grow { - flex: 1; -} diff --git a/src/vs/workbench/contrib/debug/browser/replFilter.ts b/src/vs/workbench/contrib/debug/browser/replFilter.ts index 036e0309564..50b2b862673 100644 --- a/src/vs/workbench/contrib/debug/browser/replFilter.ts +++ b/src/vs/workbench/contrib/debug/browser/replFilter.ts @@ -117,13 +117,12 @@ export class ReplFilterActionViewItem extends BaseActionViewItem { render(container: HTMLElement): void { this.container = container; - DOM.addClass(this.container, 'panel-action-tree-filter-container'); + DOM.addClass(this.container, 'repl-panel-filter-container'); this.element = DOM.append(this.container, DOM.$('')); this.element.className = this.class; this.createInput(this.element); this.updateClass(); - this.filterInputBox.inputElement.style.paddingRight = DOM.hasClass(this.element, 'small') ? '25px' : '150px'; } focus(): void { From 8ed071bc4408d64e31a44dd1be76592210555fc1 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru Date: Mon, 31 Aug 2020 12:06:37 +0200 Subject: [PATCH 706/736] Set marker properties only when settings change --- .../workbench/browser/actions/developerActions.ts | 14 +++++--------- src/vs/workbench/browser/actions/media/actions.css | 1 + 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/browser/actions/developerActions.ts b/src/vs/workbench/browser/actions/developerActions.ts index 4a9da40d32d..fdce58dd22b 100644 --- a/src/vs/workbench/browser/actions/developerActions.ts +++ b/src/vs/workbench/browser/actions/developerActions.ts @@ -123,30 +123,26 @@ class ToggleScreencastModeAction extends Action2 { const onMouseUp = domEvent(container, 'mouseup', true); const onMouseMove = domEvent(container, 'mousemove', true); - let mouseIndicatorColor: string; const updateMouseIndicatorColor = () => { - mouseIndicatorColor = configurationService.getValue('screencastMode.mouseIndicatorColor'); + const mouseIndicatorColor = configurationService.getValue('screencastMode.mouseIndicatorColor'); let style = new Option().style; style.color = mouseIndicatorColor; - if (mouseIndicatorColor === '' || !style.color) { - mouseIndicatorColor = 'red'; - } + mouseMarker.style.borderColor = (mouseIndicatorColor === '' || !style.color) ? 'red' : mouseIndicatorColor; }; let mouseIndicatorSize: number; const updateMouseIndicatorSize = () => { mouseIndicatorSize = clamp(configurationService.getValue('screencastMode.mouseIndicatorSize') || 20, 20, 100); + + mouseMarker.style.height = `${mouseIndicatorSize}px`; + mouseMarker.style.width = `${mouseIndicatorSize}px`; }; updateMouseIndicatorColor(); updateMouseIndicatorSize(); disposables.add(onMouseDown(e => { - mouseMarker.style.height = `${mouseIndicatorSize}px`; - mouseMarker.style.width = `${mouseIndicatorSize}px`; - mouseMarker.style.borderRadius = '50%'; - mouseMarker.style.borderColor = mouseIndicatorColor; mouseMarker.style.top = `${e.clientY - mouseIndicatorSize / 2}px`; mouseMarker.style.left = `${e.clientX - mouseIndicatorSize / 2}px`; mouseMarker.style.display = 'block'; diff --git a/src/vs/workbench/browser/actions/media/actions.css b/src/vs/workbench/browser/actions/media/actions.css index 2397d2d48f6..54aeeae495a 100644 --- a/src/vs/workbench/browser/actions/media/actions.css +++ b/src/vs/workbench/browser/actions/media/actions.css @@ -11,6 +11,7 @@ position: absolute; border-width: 2px; border-style: solid; + border-radius: 50%; z-index: 100000; content: ' '; pointer-events: none; From 9ef110d0fd30c1f32dc1323dfec22d3a04c70293 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 31 Aug 2020 13:56:49 +0200 Subject: [PATCH 707/736] Show config based tips in workspace recommendations --- .../extensionRecommendationsService.ts | 7 +- .../extensions/browser/extensionsActions.ts | 80 +------------------ .../extensions/browser/extensionsViews.ts | 73 +++++++++-------- .../electron-browser/extensionsViews.test.ts | 17 ++-- .../common/extensionManagement.ts | 6 +- 5 files changed, 58 insertions(+), 125 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts index 1282981f9bd..3f4cab7e871 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts @@ -163,9 +163,12 @@ export class ExtensionRecommendationsService extends Disposable implements IExte return output; } - async getConfigBasedRecommendations(): Promise { + async getConfigBasedRecommendations(): Promise<{ important: IExtensionRecommendation[], others: IExtensionRecommendation[] }> { await this.configBasedRecommendations.activate(); - return this.toExtensionRecommendations(this.configBasedRecommendations.recommendations); + return { + important: this.toExtensionRecommendations(this.configBasedRecommendations.importantRecommendations), + others: this.toExtensionRecommendations(this.configBasedRecommendations.otherRecommendations) + }; } async getOtherRecommendations(): Promise { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 6d18b730543..c773f250a3c 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -35,7 +35,6 @@ import { Color } from 'vs/base/common/color'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { ITextEditorSelection } from 'vs/platform/editor/common/editor'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { PagedModel } from 'vs/base/common/paging'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { MenuRegistry, MenuId, IMenuService } from 'vs/platform/actions/common/actions'; @@ -1829,83 +1828,6 @@ export class ShowRecommendedExtensionsAction extends Action { } } -export class InstallRecommendedExtensionsAction extends Action { - - static readonly ID = 'workbench.extensions.action.installRecommendedExtensions'; - static readonly LABEL = localize('installRecommendedExtensions', "Install Recommended Extensions"); - - private _recommendations: string[] = []; - get recommendations(): string[] { return this._recommendations; } - set recommendations(recommendations: string[]) { this._recommendations = recommendations; this.enabled = this._recommendations.length > 0; } - - constructor( - id: string, - label: string, - recommendations: string[], - private readonly searchValue: string, - private readonly source: string, - @IViewletService private readonly viewletService: IViewletService, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, - @IProductService private readonly productService: IProductService, - ) { - super(id, label, 'extension-action'); - this.recommendations = recommendations; - } - - async run(): Promise { - await new SearchExtensionsAction(this.searchValue, this.viewletService).run(); - const names = this.recommendations; - const pager = await this.extensionWorkbenchService.queryGallery({ names, source: this.source }, CancellationToken.None); - const installPromises: Promise[] = []; - const model = new PagedModel(pager); - for (let i = 0; i < pager.total; i++) { - installPromises.push(model.resolve(i, CancellationToken.None) - .then(e => this.installExtension(e))); - } - return Promise.all(installPromises); - } - - private async installExtension(extension: IExtension): Promise { - try { - if (extension.local && extension.gallery) { - if (prefersExecuteOnUI(extension.local.manifest, this.productService, this.configurationService)) { - if (this.extensionManagementServerService.localExtensionManagementServer) { - await this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(extension.gallery); - return; - } - } else if (this.extensionManagementServerService.remoteExtensionManagementServer) { - await this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.installFromGallery(extension.gallery); - return; - } - } - this.extensionWorkbenchService.open(extension, { pinned: true }); - await this.extensionWorkbenchService.install(extension); - } catch (err) { - console.error(err); - return promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.identifier.id), err, this.instantiationService); - } - } -} - -export class InstallWorkspaceRecommendedExtensionsAction extends InstallRecommendedExtensionsAction { - - constructor( - recommendations: string[], - @IViewletService viewletService: IViewletService, - @IInstantiationService instantiationService: IInstantiationService, - @IExtensionsWorkbenchService extensionWorkbenchService: IExtensionsWorkbenchService, - @IConfigurationService configurationService: IConfigurationService, - @IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService, - @IProductService productService: IProductService, - ) { - super('workbench.extensions.action.installWorkspaceRecommendedExtensions', localize('installWorkspaceRecommendedExtensions', "Install Workspace Recommended Extensions"), recommendations, '@recommended ', 'install-all-workspace-recommendations', - viewletService, instantiationService, extensionWorkbenchService, configurationService, extensionManagementServerService, productService); - } -} - export class ShowRecommendedExtensionAction extends Action { static readonly ID = 'workbench.extensions.action.showRecommendedExtension'; @@ -1918,7 +1840,7 @@ export class ShowRecommendedExtensionAction extends Action { @IViewletService private readonly viewletService: IViewletService, @IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService, ) { - super(InstallRecommendedExtensionAction.ID, InstallRecommendedExtensionAction.LABEL, undefined, false); + super(ShowRecommendedExtensionAction.ID, ShowRecommendedExtensionAction.LABEL, undefined, false); this.extensionId = extensionId; } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index fb17f22cb23..dae49c0bbca 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -17,7 +17,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { append, $, toggleClass, addClass } from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Delegate, Renderer, IExtensionsViewState } from 'vs/workbench/contrib/extensions/browser/extensionsList'; -import { IExtension, IExtensionsWorkbenchService, ExtensionState } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IExtension, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; import { Query } from 'vs/workbench/contrib/extensions/common/extensionQuery'; import { IExtensionService, toExtension } from 'vs/workbench/services/extensions/common/extensions'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -25,7 +25,7 @@ import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; -import { InstallWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, ManageExtensionAction, InstallLocalExtensionsInRemoteAction, getContextMenuActions, ExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { ConfigureWorkspaceFolderRecommendedExtensionsAction, ManageExtensionAction, InstallLocalExtensionsInRemoteAction, getContextMenuActions, ExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { WorkbenchPagedList, ListResourceNavigator } from 'vs/platform/list/browser/listService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; @@ -589,7 +589,7 @@ export class ExtensionsListView extends ViewPane { return new PagedModel([]); } - private async getInstallableRecommendations(recommendations: IExtensionRecommendation[], options: IQueryOptions, token: CancellationToken): Promise { + protected async getInstallableRecommendations(recommendations: IExtensionRecommendation[], options: IQueryOptions, token: CancellationToken): Promise { const extensions: IExtension[] = []; if (recommendations.length) { const names = recommendations.map(({ extensionId }) => extensionId); @@ -603,13 +603,25 @@ export class ExtensionsListView extends ViewPane { return extensions; } + protected async getWorkspaceRecommendations(): Promise { + const recommendations = await this.extensionRecommendationsService.getWorkspaceRecommendations(); + const { important } = await this.extensionRecommendationsService.getConfigBasedRecommendations(); + for (const configBasedRecommendation of important) { + if (recommendations.some(r => r.extensionId !== configBasedRecommendation.extensionId)) { + recommendations.push(configBasedRecommendation); + } + } + return recommendations; + } + private async getWorkspaceRecommendationsModel(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { const value = query.value.replace(/@recommended:workspace/g, '').trim().toLowerCase(); - const recommendations = await this.extensionRecommendationsService.getWorkspaceRecommendations(); + const recommendations = await this.getWorkspaceRecommendations(); const installableRecommendations = (await this.getInstallableRecommendations(recommendations, { ...options, source: 'recommendations-workspace' }, token)) .filter(extension => extension.identifier.id.toLowerCase().indexOf(value) > -1); this.telemetryService.publicLog2<{ count: number }, WorkspaceRecommendationsClassification>('extensionWorkspaceRecommendations:open', { count: installableRecommendations.length }); - return new PagedModel(installableRecommendations); + const result: IExtension[] = coalesce(recommendations.map(({ extensionId: id }) => installableRecommendations.find(i => areSameExtensions(i.identifier, { id })))); + return new PagedModel(result); } private async getKeymapRecommendationsModel(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { @@ -633,14 +645,13 @@ export class ExtensionsListView extends ViewPane { const local = (await this.extensionsWorkbenchService.queryLocal(this.server)) .filter(e => e.type === ExtensionType.User) .map(e => e.identifier.id.toLowerCase()); - const workspaceRecommendations = (await this.extensionRecommendationsService.getWorkspaceRecommendations()) + const workspaceRecommendations = (await this.getWorkspaceRecommendations()) .map(r => r.extensionId.toLowerCase()); const otherRecommendations = distinct( flatten(await Promise.all([ // Order is important this.extensionRecommendationsService.getImportantRecommendations(), - this.extensionRecommendationsService.getConfigBasedRecommendations(), this.extensionRecommendationsService.getFileBasedRecommendations(), this.extensionRecommendationsService.getOtherRecommendations() ])).filter(({ extensionId }) => !local.includes(extensionId.toLowerCase()) && !workspaceRecommendations.includes(extensionId.toLowerCase()) @@ -662,9 +673,8 @@ export class ExtensionsListView extends ViewPane { const allRecommendations = distinct( flatten(await Promise.all([ // Order is important - this.extensionRecommendationsService.getWorkspaceRecommendations(), + this.getWorkspaceRecommendations(), this.extensionRecommendationsService.getImportantRecommendations(), - this.extensionRecommendationsService.getConfigBasedRecommendations(), this.extensionRecommendationsService.getFileBasedRecommendations(), this.extensionRecommendationsService.getOtherRecommendations() ])).filter(({ extensionId }) => !local.includes(extensionId.toLowerCase()) @@ -979,20 +989,18 @@ export class RecommendedExtensionsView extends ExtensionsListView { export class WorkspaceRecommendedExtensionsView extends ExtensionsListView { private readonly recommendedExtensionsQuery = '@recommended:workspace'; - private installAllAction: InstallWorkspaceRecommendedExtensionsAction | undefined; + private installAllAction: Action | undefined; renderBody(container: HTMLElement): void { super.renderBody(container); - this._register(this.extensionRecommendationsService.onRecommendationChange(() => this.update())); - this._register(this.extensionsWorkbenchService.onChange(() => this.setRecommendationsToInstall())); - this._register(this.contextService.onDidChangeWorkbenchState(() => this.update())); + this._register(this.extensionRecommendationsService.onRecommendationChange(() => this.show(this.recommendedExtensionsQuery))); + this._register(this.contextService.onDidChangeWorkbenchState(() => this.show(this.recommendedExtensionsQuery))); } getActions(): IAction[] { if (!this.installAllAction) { - this.installAllAction = this._register(this.instantiationService.createInstance(InstallWorkspaceRecommendedExtensionsAction, [])); - this.installAllAction.class = 'codicon codicon-cloud-download'; + this.installAllAction = this._register(new Action('workbench.extensions.action.installWorkspaceRecommendedExtensions', localize('installWorkspaceRecommendedExtensions', "Install Workspace Recommended Extensions"), 'codicon codicon-cloud-download', false, () => this.installWorkspaceRecommendations())); } const configureWorkspaceFolderAction = this._register(this.instantiationService.createInstance(ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL)); @@ -1004,33 +1012,28 @@ export class WorkspaceRecommendedExtensionsView extends ExtensionsListView { let shouldShowEmptyView = query && query.trim() !== '@recommended' && query.trim() !== '@recommended:workspace'; let model = await (shouldShowEmptyView ? this.showEmptyModel() : super.show(this.recommendedExtensionsQuery)); this.setExpanded(model.length > 0); + await this.setRecommendationsToInstall(); return model; } - private update(): void { - this.show(this.recommendedExtensionsQuery); - this.setRecommendationsToInstall(); - } - private async setRecommendationsToInstall(): Promise { - const recommendations = await this.getRecommendationsToInstall(); + const installableRecommendations = await this.getInstallableWorkspaceRecommendations(); if (this.installAllAction) { - this.installAllAction.recommendations = recommendations.map(({ extensionId }) => extensionId); + this.installAllAction.enabled = installableRecommendations.length > 0; } } - private getRecommendationsToInstall(): Promise { - return this.extensionRecommendationsService.getWorkspaceRecommendations() - .then(recommendations => recommendations.filter(({ extensionId }) => { - const extension = this.extensionsWorkbenchService.local.filter(i => areSameExtensions({ id: extensionId }, i.identifier))[0]; - if (!extension - || !extension.local - || extension.state !== ExtensionState.Installed - || extension.enablementState === EnablementState.DisabledByExtensionKind - ) { - return true; - } - return false; - })); + private async getInstallableWorkspaceRecommendations() { + const installed = (await this.extensionsWorkbenchService.queryLocal()) + .filter(l => l.enablementState !== EnablementState.DisabledByExtensionKind); // Filter extensions disabled by kind + const recommendations = (await this.getWorkspaceRecommendations()) + .filter(({ extensionId }) => installed.every(local => !areSameExtensions({ id: extensionId }, local.identifier))); + return this.getInstallableRecommendations(recommendations, { source: 'install-all-workspace-recommendations' }, CancellationToken.None); } + + private async installWorkspaceRecommendations(): Promise { + const installableRecommendations = await this.getInstallableWorkspaceRecommendations(); + await Promise.all(installableRecommendations.map(extension => this.extensionManagementService.installFromGallery(extension.gallery!))); + } + } diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts index a6c10ab0fc2..e2a88db3452 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts @@ -67,6 +67,7 @@ suite('ExtensionsListView Tests', () => { const workspaceRecommendationA = aGalleryExtension('workspace-recommendation-A'); const workspaceRecommendationB = aGalleryExtension('workspace-recommendation-B'); const configBasedRecommendationA = aGalleryExtension('configbased-recommendation-A'); + const configBasedRecommendationB = aGalleryExtension('configbased-recommendation-B'); const fileBasedRecommendationA = aGalleryExtension('filebased-recommendation-A'); const fileBasedRecommendationB = aGalleryExtension('filebased-recommendation-B'); const otherRecommendationA = aGalleryExtension('other-recommendation-A'); @@ -126,9 +127,10 @@ suite('ExtensionsListView Tests', () => { { extensionId: workspaceRecommendationB.identifier.id }]); }, getConfigBasedRecommendations() { - return Promise.resolve([ - { extensionId: configBasedRecommendationA.identifier.id } - ]); + return Promise.resolve({ + important: [{ extensionId: configBasedRecommendationA.identifier.id }], + others: [{ extensionId: configBasedRecommendationB.identifier.id }], + }); }, getImportantRecommendations(): Promise { return Promise.resolve([]); @@ -141,6 +143,7 @@ suite('ExtensionsListView Tests', () => { }, getOtherRecommendations() { return Promise.resolve([ + { extensionId: configBasedRecommendationB.identifier.id }, { extensionId: otherRecommendationA.identifier.id } ]); }, @@ -336,7 +339,8 @@ suite('ExtensionsListView Tests', () => { test('Test @recommended:workspace query', () => { const workspaceRecommendedExtensions = [ workspaceRecommendationA, - workspaceRecommendationB + workspaceRecommendationB, + configBasedRecommendationA, ]; const target = instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(...workspaceRecommendedExtensions)); @@ -354,9 +358,9 @@ suite('ExtensionsListView Tests', () => { test('Test @recommended query', () => { const allRecommendedExtensions = [ - configBasedRecommendationA, fileBasedRecommendationA, fileBasedRecommendationB, + configBasedRecommendationB, otherRecommendationA ]; const target = instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(...allRecommendedExtensions)); @@ -382,7 +386,8 @@ suite('ExtensionsListView Tests', () => { configBasedRecommendationA, fileBasedRecommendationA, fileBasedRecommendationB, - otherRecommendationA + configBasedRecommendationB, + otherRecommendationA, ]; const target = instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(...allRecommendedExtensions)); diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts index c2e854e837d..f18d12e8e8b 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts @@ -132,11 +132,11 @@ export interface IExtensionRecommendationsService { readonly _serviceBrand: undefined; getAllRecommendationsWithReason(): IStringDictionary; + getImportantRecommendations(): Promise; + getOtherRecommendations(): Promise; getFileBasedRecommendations(): IExtensionRecommendation[]; getExeBasedRecommendations(exe?: string): Promise<{ important: IExtensionRecommendation[], others: IExtensionRecommendation[] }>; - getImportantRecommendations(): Promise; - getConfigBasedRecommendations(): Promise; - getOtherRecommendations(): Promise; + getConfigBasedRecommendations(): Promise<{ important: IExtensionRecommendation[], others: IExtensionRecommendation[] }>; getWorkspaceRecommendations(): Promise; getKeymapRecommendations(): IExtensionRecommendation[]; From 20c94eb37c0ce5dbb6d226469f71a633ed6e6637 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 31 Aug 2020 14:57:59 +0200 Subject: [PATCH 708/736] Fix tooltip + hover on icon in tree view Fixes #105647 --- src/vs/workbench/contrib/views/browser/treeView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/views/browser/treeView.ts b/src/vs/workbench/contrib/views/browser/treeView.ts index 0a6fa487993..67a16309494 100644 --- a/src/vs/workbench/contrib/views/browser/treeView.ts +++ b/src/vs/workbench/contrib/views/browser/treeView.ts @@ -788,7 +788,6 @@ class TreeRenderer extends Disposable implements ITreeRenderer{ $treeViewId: this.treeViewId, $treeItemHandle: node.handle }; templateData.actionBar.push(this.menus.getResourceActions(node), { icon: true, label: false }); From 6aafc4b7aae07591580da9370fd24b2029fc25d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 31 Aug 2020 15:20:47 +0200 Subject: [PATCH 709/736] fixes #104762 --- extensions/git/package.json | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 1704a89e7e3..1e577006c1f 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -754,13 +754,23 @@ "when": "scmProvider == git" }, { - "command": "git.checkout", - "group": "1_header", + "command": "git.pull", + "group": "1_header@1", + "when": "scmProvider == git" + }, + { + "command": "git.push", + "group": "1_header@2", "when": "scmProvider == git" }, { "command": "git.clone", - "group": "1_header", + "group": "1_header@3", + "when": "scmProvider == git" + }, + { + "command": "git.checkout", + "group": "1_header@4", "when": "scmProvider == git" }, { From 4b543447ae13ea174070f877b4bc4bd971823e5e Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 31 Aug 2020 15:32:46 +0200 Subject: [PATCH 710/736] debug: respect __vscodeVariableMenuContext #70377 --- .../contrib/debug/browser/variablesView.ts | 21 ++++++++++--------- .../workbench/contrib/debug/common/debug.ts | 1 + .../contrib/debug/common/debugModel.ts | 15 ++++++++----- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index 6059c435dd1..a001777f000 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -8,7 +8,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import * as dom from 'vs/base/browser/dom'; import { CollapseAction } from 'vs/workbench/browser/viewlet'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; -import { IDebugService, IExpression, IScope, CONTEXT_VARIABLES_FOCUSED, IStackFrame } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, IExpression, IScope, CONTEXT_VARIABLES_FOCUSED, IStackFrame, CONTEXT_DEBUG_PROTOCOL_VARIABLE_MENU_CONTEXT } from 'vs/workbench/contrib/debug/common/debug'; import { Variable, Scope, ErrorScope, StackFrame } from 'vs/workbench/contrib/debug/common/debugModel'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -27,7 +27,7 @@ import { IAsyncDataTreeViewState } from 'vs/base/browser/ui/tree/asyncDataTree'; import { FuzzyScore, createMatches } from 'vs/base/common/filters'; import { HighlightedLabel, IHighlight } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { dispose } from 'vs/base/common/lifecycle'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; @@ -50,6 +50,7 @@ export class VariablesView extends ViewPane { private savedViewState = new Map(); private autoExpandedScopes = new Set(); private menu: IMenu; + private debugProtocolVariableMenuContext: IContextKey; constructor( options: IViewletViewOptions, @@ -70,6 +71,7 @@ export class VariablesView extends ViewPane { this.menu = menuService.createMenu(MenuId.DebugVariablesContext, contextKeyService); this._register(this.menu); + this.debugProtocolVariableMenuContext = CONTEXT_DEBUG_PROTOCOL_VARIABLE_MENU_CONTEXT.bindTo(contextKeyService); // Use scheduler to prevent unnecessary flashing this.onFocusStackFrameScheduler = new RunOnceScheduler(async () => { @@ -193,20 +195,18 @@ export class VariablesView extends ViewPane { const actions: IAction[] = []; const session = this.debugService.getViewModel().focusedSession; if (session && session.capabilities.supportsSetVariable) { - actions.push(new Action('workbench.setValue', nls.localize('setValue', "Set Value"), undefined, true, () => { + actions.push(new Action('workbench.setValue', nls.localize('setValue', "Set Value"), undefined, true, async () => { this.debugService.getViewModel().setSelectedExpression(variable); - return Promise.resolve(); })); } actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, variable, 'variables')); if (variable.evaluateName) { - actions.push(new Action('debug.copyEvaluatePath', nls.localize('copyAsExpression', "Copy as Expression"), undefined, true, () => { - return this.clipboardService.writeText(variable.evaluateName!); + actions.push(new Action('debug.copyEvaluatePath', nls.localize('copyAsExpression', "Copy as Expression"), undefined, true, async () => { + await this.clipboardService.writeText(variable.evaluateName!); })); actions.push(new Separator()); - actions.push(new Action('debug.addToWatchExpressions', nls.localize('addToWatchExpressions', "Add to Watch"), undefined, true, () => { + actions.push(new Action('debug.addToWatchExpressions', nls.localize('addToWatchExpressions', "Add to Watch"), undefined, true, async () => { this.debugService.addWatchExpression(variable.evaluateName); - return Promise.resolve(undefined); })); } if (session && session.capabilities.supportsDataBreakpoints) { @@ -214,8 +214,8 @@ export class VariablesView extends ViewPane { const dataid = response?.dataId; if (response && dataid) { actions.push(new Separator()); - actions.push(new Action('debug.breakWhenValueChanges', nls.localize('breakWhenValueChanges', "Break When Value Changes"), undefined, true, () => { - return this.debugService.addDataBreakpoint(response.description, dataid, !!response.canPersist, response.accessTypes); + actions.push(new Action('debug.breakWhenValueChanges', nls.localize('breakWhenValueChanges', "Break When Value Changes"), undefined, true, async () => { + await this.debugService.addDataBreakpoint(response.description, dataid, !!response.canPersist, response.accessTypes); })); } } @@ -225,6 +225,7 @@ export class VariablesView extends ViewPane { variable: variable.toDebugProtocolObject() }; const actionsDisposable = createAndFillInContextMenuActions(this.menu, { arg: context, shouldForwardArgs: false }, actions, this.contextMenuService); + this.debugProtocolVariableMenuContext.set(variable.variableMenuContext || ''); this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 6467c93b443..8092d6da302 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -60,6 +60,7 @@ export const CONTEXT_JUMP_TO_CURSOR_SUPPORTED = new RawContextKey('jump export const CONTEXT_STEP_INTO_TARGETS_SUPPORTED = new RawContextKey('stepIntoTargetsSupported', false); export const CONTEXT_BREAKPOINTS_EXIST = new RawContextKey('breakpointsExist', false); export const CONTEXT_DEBUGGERS_AVAILABLE = new RawContextKey('debuggersAvailable', false); +export const CONTEXT_DEBUG_PROTOCOL_VARIABLE_MENU_CONTEXT = new RawContextKey('debugProtocolVariableMenuContext', undefined); export const EDITOR_CONTRIBUTION_ID = 'editor.contrib.debug'; export const BREAKPOINT_EDITOR_CONTRIBUTION_ID = 'editor.contrib.breakpoint'; diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 5ba4e274600..b47d112dca0 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -24,6 +24,10 @@ import { mixin } from 'vs/base/common/objects'; import { DebugStorage } from 'vs/workbench/contrib/debug/common/debugStorage'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; +interface IDebugProtocolVariableWithContext extends DebugProtocol.Variable { + __vscodeVariableMenuContext?: string; +} + export class ExpressionContainer implements IExpressionContainer { public static readonly allValues = new Map(); @@ -86,7 +90,7 @@ export class ExpressionContainer implements IExpressionContainer { for (let i = 0; i < numberOfChunks; i++) { const start = (this.startOfVariables || 0) + i * chunkSize; const count = Math.min(chunkSize, this.indexedVariables - i * chunkSize); - children.push(new Variable(this.session, this.threadId, this, this.reference, `[${start}..${start + count - 1}]`, '', '', undefined, count, { kind: 'virtual' }, undefined, true, start)); + children.push(new Variable(this.session, this.threadId, this, this.reference, `[${start}..${start + count - 1}]`, '', '', undefined, count, { kind: 'virtual' }, undefined, undefined, true, start)); } return children; @@ -117,14 +121,14 @@ export class ExpressionContainer implements IExpressionContainer { try { const response = await this.session!.variables(this.reference || 0, this.threadId, filter, start, count); return response && response.body && response.body.variables - ? distinct(response.body.variables.filter(v => !!v), v => v.name).map(v => { + ? distinct(response.body.variables.filter(v => !!v), v => v.name).map((v: IDebugProtocolVariableWithContext) => { if (isString(v.value) && isString(v.name) && typeof v.variablesReference === 'number') { - return new Variable(this.session, this.threadId, this, v.variablesReference, v.name, v.evaluateName, v.value, v.namedVariables, v.indexedVariables, v.presentationHint, v.type); + return new Variable(this.session, this.threadId, this, v.variablesReference, v.name, v.evaluateName, v.value, v.namedVariables, v.indexedVariables, v.presentationHint, v.type, v.__vscodeVariableMenuContext); } - return new Variable(this.session, this.threadId, this, 0, '', undefined, nls.localize('invalidVariableAttributes', "Invalid variable attributes"), 0, 0, { kind: 'virtual' }, undefined, false); + return new Variable(this.session, this.threadId, this, 0, '', undefined, nls.localize('invalidVariableAttributes', "Invalid variable attributes"), 0, 0, { kind: 'virtual' }, undefined, undefined, false); }) : []; } catch (e) { - return [new Variable(this.session, this.threadId, this, 0, '', undefined, e.message, 0, 0, { kind: 'virtual' }, undefined, false)]; + return [new Variable(this.session, this.threadId, this, 0, '', undefined, e.message, 0, 0, { kind: 'virtual' }, undefined, undefined, false)]; } } @@ -218,6 +222,7 @@ export class Variable extends ExpressionContainer implements IExpression { indexedVariables: number | undefined, public presentationHint: DebugProtocol.VariablePresentationHint | undefined, public type: string | undefined = undefined, + public variableMenuContext: string | undefined = undefined, public available = true, startOfVariables = 0 ) { From def4753d5e9e611be2dafa220112a5011d79f34f Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 31 Aug 2020 15:14:49 +0200 Subject: [PATCH 711/736] Use appendText for markdown when tree tooltip is string Fixes #105646 --- src/vs/workbench/contrib/views/browser/treeView.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/views/browser/treeView.ts b/src/vs/workbench/contrib/views/browser/treeView.ts index 67a16309494..c620932f2cf 100644 --- a/src/vs/workbench/contrib/views/browser/treeView.ts +++ b/src/vs/workbench/contrib/views/browser/treeView.ts @@ -40,6 +40,7 @@ import { isFalsyOrWhitespace } from 'vs/base/common/strings'; import { SIDE_BAR_BACKGROUND, PANEL_BACKGROUND } from 'vs/workbench/common/theme'; import { IHoverService, IHoverOptions, IHoverTarget } from 'vs/workbench/services/hover/browser/hover'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; class Root implements ITreeItem { label = { label: 'root' }; @@ -824,7 +825,13 @@ class TreeRenderer extends Disposable implements ITreeRenderer Date: Mon, 31 Aug 2020 16:30:29 +0200 Subject: [PATCH 712/736] debt - move keyboard settings for touchbar into desktop.contribution --- src/vs/workbench/electron-browser/window.ts | 4 +- .../electron-sandbox/desktop.contribution.ts | 25 +++++++++++++ .../keybinding.contribution.ts | 37 ------------------- src/vs/workbench/workbench.desktop.main.ts | 1 - 4 files changed, 27 insertions(+), 40 deletions(-) delete mode 100644 src/vs/workbench/services/keybinding/electron-browser/keybinding.contribution.ts diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 1056c89d5b1..b32ba930cc3 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -647,13 +647,13 @@ class NativeMenubarControl extends MenubarControl { (async () => { this.recentlyOpened = await this.workspacesService.getRecentlyOpened(); - this.doUpdateMenubar(true); + this.doUpdateMenubar(); })(); this.registerListeners(); } - protected doUpdateMenubar(firstTime: boolean): void { + protected doUpdateMenubar(): void { // Since the native menubar is shared between windows (main process) // only allow the focused window to update the menubar if (!this.hostService.hasFocus) { diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index 61102025606..07fe262c796 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -314,6 +314,31 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; } } }); + + // Keybinding + registry.registerConfiguration({ + 'id': 'keyboard', + 'order': 15, + 'type': 'object', + 'title': nls.localize('keyboardConfigurationTitle', "Keyboard"), + 'properties': { + 'keyboard.touchbar.enabled': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('touchbar.enabled', "Enables the macOS touchbar buttons on the keyboard if available."), + 'included': isMacintosh + }, + 'keyboard.touchbar.ignored': { + 'type': 'array', + 'items': { + 'type': 'string' + }, + 'default': [], + 'markdownDescription': nls.localize('touchbar.ignored', 'A set of identifiers for entries in the touchbar that should not show up (for example `workbench.action.navigateBack`.'), + 'included': isMacintosh + } + } + }); })(); // JSON Schemas diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybinding.contribution.ts b/src/vs/workbench/services/keybinding/electron-browser/keybinding.contribution.ts deleted file mode 100644 index 230ed0ed670..00000000000 --- a/src/vs/workbench/services/keybinding/electron-browser/keybinding.contribution.ts +++ /dev/null @@ -1,37 +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 { release } from 'os'; -import { OS, OperatingSystem } from 'vs/base/common/platform'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { Extensions as ConfigExtensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; - -const configurationRegistry = Registry.as(ConfigExtensions.Configuration); -const keyboardConfiguration: IConfigurationNode = { - 'id': 'keyboard', - 'order': 15, - 'type': 'object', - 'title': nls.localize('keyboardConfigurationTitle', "Keyboard"), - 'properties': { - 'keyboard.touchbar.enabled': { - 'type': 'boolean', - 'default': true, - 'description': nls.localize('touchbar.enabled', "Enables the macOS touchbar buttons on the keyboard if available."), - 'included': OS === OperatingSystem.Macintosh && parseFloat(release()) >= 16 // Minimum: macOS Sierra (10.12.x = darwin 16.x) - }, - 'keyboard.touchbar.ignored': { - 'type': 'array', - 'items': { - 'type': 'string' - }, - 'default': [], - 'markdownDescription': nls.localize('touchbar.ignored', 'A set of identifiers for entries in the touchbar that should not show up (for example `workbench.action.navigateBack`.'), - 'included': OS === OperatingSystem.Macintosh && parseFloat(release()) >= 16 // Minimum: macOS Sierra (10.12.x = darwin 16.x) - } - } -}; - -configurationRegistry.registerConfiguration(keyboardConfiguration); diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 8c642a03f3e..c9eec9b1ae1 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -41,7 +41,6 @@ import 'vs/workbench/services/output/electron-browser/outputChannelModelService' import 'vs/workbench/services/textfile/electron-browser/nativeTextFileService'; import 'vs/workbench/services/dialogs/electron-browser/dialogService'; import 'vs/workbench/services/keybinding/electron-browser/nativeKeymapService'; -import 'vs/workbench/services/keybinding/electron-browser/keybinding.contribution'; import 'vs/workbench/services/extensions/electron-browser/extensionService'; import 'vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService'; import 'vs/workbench/services/extensionManagement/electron-browser/extensionTipsService'; From d08e158b8df987e0697bf41669b93a7621a83880 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 31 Aug 2020 16:30:47 +0200 Subject: [PATCH 713/736] Investigate #104554 --- .../src/singlefolder-tests/window.test.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) 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 8c8d5e64ca7..9187b34e548 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts @@ -429,6 +429,8 @@ suite('vscode API - window', () => { }); test('showQuickPick, select first two', async function () { + const label = 'showQuickPick, select first two'; + let i = 0; const resolves: ((value: string) => void)[] = []; let done: () => void; const unexpected = new Promise((resolve, reject) => { @@ -440,16 +442,26 @@ suite('vscode API - window', () => { canPickMany: true }); const first = new Promise(resolve => resolves.push(resolve)); - await new Promise(resolve => setTimeout(resolve, 10)); // Allow UI to update. + console.log(`${label}: ${++i}`); + await new Promise(resolve => setTimeout(resolve, 100)); // Allow UI to update. + console.log(`${label}: ${++i}`); await commands.executeCommand('workbench.action.quickOpenSelectNext'); + console.log(`${label}: ${++i}`); assert.equal(await first, 'eins'); + console.log(`${label}: ${++i}`); await commands.executeCommand('workbench.action.quickPickManyToggle'); + console.log(`${label}: ${++i}`); const second = new Promise(resolve => resolves.push(resolve)); await commands.executeCommand('workbench.action.quickOpenSelectNext'); + console.log(`${label}: ${++i}`); assert.equal(await second, 'zwei'); + console.log(`${label}: ${++i}`); await commands.executeCommand('workbench.action.quickPickManyToggle'); + console.log(`${label}: ${++i}`); await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + console.log(`${label}: ${++i}`); assert.deepStrictEqual(await picks, ['eins', 'zwei']); + console.log(`${label}: ${++i}`); done!(); return unexpected; }); From 38d8d797ec26f46dd9fc85094152ca15e0336b71 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 31 Aug 2020 16:32:56 +0200 Subject: [PATCH 714/736] debt - ExtHostWindow implements ExtHostWindowShape, not itself --- src/vs/workbench/api/common/extHostWindow.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHostWindow.ts b/src/vs/workbench/api/common/extHostWindow.ts index b9f1cdee4b8..a8c05964b01 100644 --- a/src/vs/workbench/api/common/extHostWindow.ts +++ b/src/vs/workbench/api/common/extHostWindow.ts @@ -12,7 +12,7 @@ import { isFalsyOrWhitespace } from 'vs/base/common/strings'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; -export class ExtHostWindow implements IExtHostWindow { +export class ExtHostWindow implements ExtHostWindowShape { private static InitialState: WindowState = { focused: true From c0f74442437a5904c349d7366a0607e11dc32d69 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 31 Aug 2020 16:42:56 +0200 Subject: [PATCH 715/736] remove unused interface --- .../contrib/notebook/common/notebookEditorModel.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts index 9ef0ab0a5ff..f2ec8b58e1e 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts @@ -18,13 +18,6 @@ import { Schemas } from 'vs/base/common/network'; import { IFileStatWithMetadata, IFileService } from 'vs/platform/files/common/files'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -export interface INotebookEditorModelManager { - models: NotebookEditorModel[]; - - resolve(resource: URI, viewType: string, editorId?: string): Promise; - - get(resource: URI): NotebookEditorModel | undefined; -} export interface INotebookLoadOptions { /** From 523034152d5b380edf4fa83677b466df5caa13a1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 31 Aug 2020 16:45:02 +0200 Subject: [PATCH 716/736] :lipstick: --- .../contrib/notebook/common/notebookEditorModel.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts index f2ec8b58e1e..472ba0217a6 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts @@ -121,7 +121,7 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN async load(options?: INotebookLoadOptions): Promise { if (options?.forceReadFromDisk) { - return this.loadFromProvider(true, undefined, undefined); + return this._loadFromProvider(true, undefined, undefined); } if (this.isResolved()) { @@ -134,10 +134,10 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN return this; // Make sure meanwhile someone else did not succeed in loading } - return this.loadFromProvider(false, options?.editorId, backup?.meta?.backupId); + return this._loadFromProvider(false, options?.editorId, backup?.meta?.backupId); } - private async loadFromProvider(forceReloadFromDisk: boolean, editorId: string | undefined, backupId: string | undefined) { + private async _loadFromProvider(forceReloadFromDisk: boolean, editorId: string | undefined, backupId: string | undefined) { const notebook = await this._notebookService.resolveNotebook(this.viewType!, this.resource, forceReloadFromDisk, editorId, backupId); this._notebook = notebook!; const newStats = await this._resolveStats(this.resource); @@ -254,10 +254,5 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN } catch (e) { return undefined; } - - } - - dispose() { - super.dispose(); } } From d4248f3df5f486603c65aa89e4d061bf23e15e48 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 31 Aug 2020 16:45:32 +0200 Subject: [PATCH 717/736] Run menu does not remember dynamic launch configurations fixes #96293 --- .../browser/debugConfigurationManager.ts | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index e28eaf83505..ed6e487941d 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -46,6 +46,7 @@ jsonRegistry.registerSchema(launchSchemaId, launchSchema); const DEBUG_SELECTED_CONFIG_NAME_KEY = 'debug.selectedconfigname'; const DEBUG_SELECTED_ROOT = 'debug.selectedroot'; +const DEBUG_SELECTED_DYNAMIC_CONFIG = 'debug.selecteddynamicconfig'; interface IDynamicPickItem { label: string, launch: ILaunch, config: IConfig } @@ -83,23 +84,30 @@ export class ConfigurationManager implements IConfigurationManager { this.toDispose = []; this.initLaunches(); this.registerListeners(); - const previousSelectedRoot = this.storageService.get(DEBUG_SELECTED_ROOT, StorageScope.WORKSPACE); - const previousSelectedLaunch = this.launches.find(l => l.uri.toString() === previousSelectedRoot); this.debugConfigurationTypeContext = CONTEXT_DEBUG_CONFIGURATION_TYPE.bindTo(contextKeyService); this.debuggersAvailable = CONTEXT_DEBUGGERS_AVAILABLE.bindTo(contextKeyService); - if (previousSelectedLaunch && previousSelectedLaunch.getConfigurationNames().length) { - this.selectConfiguration(previousSelectedLaunch, this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE)); - } else if (this.launches.length > 0) { - this.selectConfiguration(undefined); - } + } // debuggers registerDebugAdapterFactory(debugTypes: string[], debugAdapterLauncher: IDebugAdapterFactory): IDisposable { + const firstRegistration = this.debugAdapterFactories.size === 0; debugTypes.forEach(debugType => this.debugAdapterFactories.set(debugType, debugAdapterLauncher)); this.debuggersAvailable.set(this.debugAdapterFactories.size > 0); this._onDidRegisterDebugger.fire(); + if (firstRegistration) { + const previousSelectedRoot = this.storageService.get(DEBUG_SELECTED_ROOT, StorageScope.WORKSPACE); + const previousSelectedLaunch = this.launches.find(l => l.uri.toString() === previousSelectedRoot); + if (previousSelectedLaunch && previousSelectedLaunch.getConfigurationNames().length) { + const name = this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE); + const configStr = this.storageService.get(DEBUG_SELECTED_DYNAMIC_CONFIG, StorageScope.WORKSPACE); + const config = configStr ? JSON.parse(configStr) : undefined; + this.selectConfiguration(previousSelectedLaunch, name, config); + } else if (this.launches.length > 0) { + this.selectConfiguration(undefined); + } + } return { dispose: () => { @@ -534,6 +542,12 @@ export class ConfigurationManager implements IConfigurationManager { } this.selectedConfig = config; + if (config) { + // Only dynamic configurations get passed in the selectConfiguration. We should store them #96293 + this.storageService.store(DEBUG_SELECTED_DYNAMIC_CONFIG, JSON.stringify(config), StorageScope.WORKSPACE); + } else { + this.storageService.remove(DEBUG_SELECTED_DYNAMIC_CONFIG, StorageScope.WORKSPACE); + } const configForType = this.selectedConfig || (this.selectedLaunch && this.selectedName ? this.selectedLaunch.getConfiguration(this.selectedName) : undefined); if (configForType) { this.debugConfigurationTypeContext.set(configForType.type); From bc797a3b0b664f32b66890d403bdae368ff2a81e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 31 Aug 2020 16:58:21 +0200 Subject: [PATCH 718/736] sandbox - remove dependency to native keymap on startup --- src/vs/code/electron-main/app.ts | 2 +- src/vs/platform/windows/electron-main/windows.ts | 2 +- src/vs/workbench/electron-browser/desktop.main.ts | 4 ---- src/vs/workbench/electron-browser/window.ts | 6 ------ .../keybinding/electron-browser/nativeKeymapService.ts | 7 ++++++- 5 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 384586f6f41..e9c387146e8 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -303,7 +303,7 @@ export class CodeApplication extends Disposable { const nativeKeymap = await import('native-keymap'); nativeKeymap.onDidChangeKeyboardLayout(() => { if (this.windowsMainService) { - this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', false); + this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged'); } }); })(); diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 8df91e318a8..bf481595ade 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -106,7 +106,7 @@ export interface IWindowsMainService { openExtensionDevelopmentHostWindow(extensionDevelopmentPath: string[], openConfig: IOpenConfiguration): ICodeWindow[]; sendToFocused(channel: string, ...args: any[]): void; - sendToAll(channel: string, payload: any, windowIdsToIgnore?: number[]): void; + sendToAll(channel: string, payload?: any, windowIdsToIgnore?: number[]): void; getLastActiveWindow(): ICodeWindow | undefined; diff --git a/src/vs/workbench/electron-browser/desktop.main.ts b/src/vs/workbench/electron-browser/desktop.main.ts index a0ed6c01538..20a22c30d0a 100644 --- a/src/vs/workbench/electron-browser/desktop.main.ts +++ b/src/vs/workbench/electron-browser/desktop.main.ts @@ -17,7 +17,6 @@ import { WorkspaceService } from 'vs/workbench/services/configuration/browser/co import { NativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/nativeKeymapService'; import { INativeWindowConfiguration } from 'vs/platform/windows/node/window'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceInitializationPayload, ISingleFolderWorkspaceInitializationPayload, reviveWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { ILogService } from 'vs/platform/log/common/log'; @@ -77,9 +76,6 @@ class DesktopMain extends Disposable { setZoomFactor(zoomLevelToZoomFactor(zoomLevel)); setZoomLevel(zoomLevel, true /* isTrusted */); setFullscreen(!!this.environmentService.configuration.fullscreen); - - // Keyboard support - KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged(); } private reviveUris() { diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index b32ba930cc3..4094d919e43 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -20,7 +20,6 @@ import { applyZoom } from 'vs/platform/windows/electron-sandbox/window'; import { setFullscreen, getZoomLevel } from 'vs/base/browser/browser'; import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; -import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/nativeKeymapService'; import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { IMenuService, MenuId, IMenu, MenuItemAction, ICommandAction, SubmenuItemAction, MenuRegistry } from 'vs/platform/actions/common/actions'; @@ -210,11 +209,6 @@ export class NativeWindow extends Disposable { this.themeService.setOSHighContrast(false); }); - // keyboard layout changed event - ipcRenderer.on('vscode:keyboardLayoutChanged', () => { - KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged(); - }); - // accessibility support changed event ipcRenderer.on('vscode:accessibilitySupportChanged', (event: unknown, accessibilitySupportEnabled: boolean) => { this.accessibilityService.setAccessibilitySupport(accessibilitySupportEnabled ? AccessibilitySupport.Enabled : AccessibilitySupport.Disabled); diff --git a/src/vs/workbench/services/keybinding/electron-browser/nativeKeymapService.ts b/src/vs/workbench/services/keybinding/electron-browser/nativeKeymapService.ts index 6ef2edda43d..ab410a95dc5 100644 --- a/src/vs/workbench/services/keybinding/electron-browser/nativeKeymapService.ts +++ b/src/vs/workbench/services/keybinding/electron-browser/nativeKeymapService.ts @@ -15,6 +15,7 @@ import { OS, OperatingSystem } from 'vs/base/common/platform'; import { WindowsKeyboardMapper, windowsKeyboardMappingEquals } from 'vs/workbench/services/keybinding/common/windowsKeyboardMapper'; import { MacLinuxKeyboardMapper, macLinuxKeyboardMappingEquals, IMacLinuxKeyboardMapping } from 'vs/workbench/services/keybinding/common/macLinuxKeyboardMapper'; import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; +import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; export class KeyboardMapperFactory { public static readonly INSTANCE = new KeyboardMapperFactory(); @@ -134,7 +135,7 @@ export class KeyboardMapperFactory { class NativeKeymapService extends Disposable implements IKeymapService { public _serviceBrand: undefined; - private readonly _onDidChangeKeyboardMapper = new Emitter(); + private readonly _onDidChangeKeyboardMapper = this._register(new Emitter()); public readonly onDidChangeKeyboardMapper: Event = this._onDidChangeKeyboardMapper.event; constructor() { @@ -143,6 +144,10 @@ class NativeKeymapService extends Disposable implements IKeymapService { this._register(KeyboardMapperFactory.INSTANCE.onDidChangeKeyboardMapper(() => { this._onDidChangeKeyboardMapper.fire(); })); + + ipcRenderer.on('vscode:keyboardLayoutChanged', () => { + KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged(); + }); } getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper { From e2d5e90ec44948a68a8887aa680ec7421550dfe6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 31 Aug 2020 17:10:19 +0200 Subject: [PATCH 719/736] debt - move extension debug IPC channel closer to interface --- src/vs/code/electron-main/app.ts | 101 +--------------- .../electron-main/extensionHostDebugIpc.ts | 109 ++++++++++++++++++ 2 files changed, 110 insertions(+), 100 deletions(-) create mode 100644 src/vs/platform/debug/electron-main/extensionHostDebugIpc.ts diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index e9c387146e8..f807ca49def 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -68,11 +68,11 @@ import { statSync } from 'fs'; import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsIpc'; import { IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService'; import { ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc'; +import { ElectronExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/electron-main/extensionHostDebugIpc'; import { IElectronMainService, ElectronMainService } from 'vs/platform/electron/electron-main/electronMainService'; import { ISharedProcessMainService, SharedProcessMainService } from 'vs/platform/ipc/electron-main/sharedProcessMainService'; import { IDialogMainService, DialogMainService } from 'vs/platform/dialogs/electron-main/dialogs'; import { withNullAsUndefined } from 'vs/base/common/types'; -import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; import { coalesce } from 'vs/base/common/arrays'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { StorageKeysSyncRegistryChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; @@ -80,8 +80,6 @@ import { INativeEnvironmentService } from 'vs/platform/environment/node/environm import { mnemonicButtonLabel, getPathLabel } from 'vs/base/common/labels'; import { WebviewMainService } from 'vs/platform/webview/electron-main/webviewMainService'; import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; -import { createServer, AddressInfo } from 'net'; -import { IOpenExtensionWindowResult } from 'vs/platform/debug/common/extensionHostDebug'; import { IFileService } from 'vs/platform/files/common/files'; import { stripComments } from 'vs/base/common/json'; import { generateUuid } from 'vs/base/common/uuid'; @@ -867,100 +865,3 @@ export class CodeApplication extends Disposable { }); } } - -class ElectronExtensionHostDebugBroadcastChannel extends ExtensionHostDebugBroadcastChannel { - - constructor(private windowsMainService: IWindowsMainService) { - super(); - } - - call(ctx: TContext, command: string, arg?: any): Promise { - if (command === 'openExtensionDevelopmentHostWindow') { - return this.openExtensionDevelopmentHostWindow(arg[0], arg[1], arg[2]); - } else { - return super.call(ctx, command, arg); - } - } - - private async openExtensionDevelopmentHostWindow(args: string[], env: IProcessEnvironment, debugRenderer: boolean): Promise { - const pargs = parseArgs(args, OPTIONS); - const extDevPaths = pargs.extensionDevelopmentPath; - if (!extDevPaths) { - return {}; - } - - const [codeWindow] = this.windowsMainService.openExtensionDevelopmentHostWindow(extDevPaths, { - context: OpenContext.API, - cli: pargs, - userEnv: Object.keys(env).length > 0 ? env : undefined - }); - - if (!debugRenderer) { - return {}; - } - - const debug = codeWindow.win.webContents.debugger; - - let listeners = debug.isAttached() ? Infinity : 0; - const server = createServer(listener => { - if (listeners++ === 0) { - debug.attach(); - } - - let closed = false; - const writeMessage = (message: object) => { - if (!closed) { // in case sendCommand promises settle after closed - listener.write(JSON.stringify(message) + '\0'); // null-delimited, CDP-compatible - } - }; - - const onMessage = (_event: Event, method: string, params: unknown, sessionId?: string) => - writeMessage(({ method, params, sessionId })); - - codeWindow.win.on('close', () => { - debug.removeListener('message', onMessage); - listener.end(); - closed = true; - }); - - debug.addListener('message', onMessage); - - let buf = Buffer.alloc(0); - listener.on('data', data => { - buf = Buffer.concat([buf, data]); - for (let delimiter = buf.indexOf(0); delimiter !== -1; delimiter = buf.indexOf(0)) { - let data: { id: number; sessionId: string; params: {} }; - try { - const contents = buf.slice(0, delimiter).toString('utf8'); - buf = buf.slice(delimiter + 1); - data = JSON.parse(contents); - } catch (e) { - console.error('error reading cdp line', e); - } - - // depends on a new API for which electron.d.ts has not been updated: - // @ts-ignore - debug.sendCommand(data.method, data.params, data.sessionId) - .then((result: object) => writeMessage({ id: data.id, sessionId: data.sessionId, result })) - .catch((error: Error) => writeMessage({ id: data.id, sessionId: data.sessionId, error: { code: 0, message: error.message } })); - } - }); - - listener.on('error', err => { - console.error('error on cdp pipe:', err); - }); - - listener.on('close', () => { - closed = true; - if (--listeners === 0) { - debug.detach(); - } - }); - }); - - await new Promise(r => server.listen(0, r)); - codeWindow.win.on('close', () => server.close()); - - return { rendererDebugPort: (server.address() as AddressInfo).port }; - } -} diff --git a/src/vs/platform/debug/electron-main/extensionHostDebugIpc.ts b/src/vs/platform/debug/electron-main/extensionHostDebugIpc.ts new file mode 100644 index 00000000000..923305acd75 --- /dev/null +++ b/src/vs/platform/debug/electron-main/extensionHostDebugIpc.ts @@ -0,0 +1,109 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IOpenExtensionWindowResult } from 'vs/platform/debug/common/extensionHostDebug'; +import { IProcessEnvironment } from 'vs/base/common/platform'; +import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; +import { createServer, AddressInfo } from 'net'; +import { ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc'; +import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; +import { OpenContext } from 'vs/platform/windows/node/window'; + +export class ElectronExtensionHostDebugBroadcastChannel extends ExtensionHostDebugBroadcastChannel { + + constructor(private windowsMainService: IWindowsMainService) { + super(); + } + + call(ctx: TContext, command: string, arg?: any): Promise { + if (command === 'openExtensionDevelopmentHostWindow') { + return this.openExtensionDevelopmentHostWindow(arg[0], arg[1], arg[2]); + } else { + return super.call(ctx, command, arg); + } + } + + private async openExtensionDevelopmentHostWindow(args: string[], env: IProcessEnvironment, debugRenderer: boolean): Promise { + const pargs = parseArgs(args, OPTIONS); + const extDevPaths = pargs.extensionDevelopmentPath; + if (!extDevPaths) { + return {}; + } + + const [codeWindow] = this.windowsMainService.openExtensionDevelopmentHostWindow(extDevPaths, { + context: OpenContext.API, + cli: pargs, + userEnv: Object.keys(env).length > 0 ? env : undefined + }); + + if (!debugRenderer) { + return {}; + } + + const debug = codeWindow.win.webContents.debugger; + + let listeners = debug.isAttached() ? Infinity : 0; + const server = createServer(listener => { + if (listeners++ === 0) { + debug.attach(); + } + + let closed = false; + const writeMessage = (message: object) => { + if (!closed) { // in case sendCommand promises settle after closed + listener.write(JSON.stringify(message) + '\0'); // null-delimited, CDP-compatible + } + }; + + const onMessage = (_event: Event, method: string, params: unknown, sessionId?: string) => + writeMessage(({ method, params, sessionId })); + + codeWindow.win.on('close', () => { + debug.removeListener('message', onMessage); + listener.end(); + closed = true; + }); + + debug.addListener('message', onMessage); + + let buf = Buffer.alloc(0); + listener.on('data', data => { + buf = Buffer.concat([buf, data]); + for (let delimiter = buf.indexOf(0); delimiter !== -1; delimiter = buf.indexOf(0)) { + let data: { id: number; sessionId: string; params: {} }; + try { + const contents = buf.slice(0, delimiter).toString('utf8'); + buf = buf.slice(delimiter + 1); + data = JSON.parse(contents); + } catch (e) { + console.error('error reading cdp line', e); + } + + // depends on a new API for which electron.d.ts has not been updated: + // @ts-ignore + debug.sendCommand(data.method, data.params, data.sessionId) + .then((result: object) => writeMessage({ id: data.id, sessionId: data.sessionId, result })) + .catch((error: Error) => writeMessage({ id: data.id, sessionId: data.sessionId, error: { code: 0, message: error.message } })); + } + }); + + listener.on('error', err => { + console.error('error on cdp pipe:', err); + }); + + listener.on('close', () => { + closed = true; + if (--listeners === 0) { + debug.detach(); + } + }); + }); + + await new Promise(r => server.listen(0, r)); + codeWindow.win.on('close', () => server.close()); + + return { rendererDebugPort: (server.address() as AddressInfo).port }; + } +} From a1df84ad20d3afdb5f6dc8bb26b8b5850383dd1f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 31 Aug 2020 17:16:45 +0200 Subject: [PATCH 720/736] :lipstick: strict typings, namings --- .../notebook/browser/notebookServiceImpl.ts | 22 +++++------- .../notebook/common/notebookEditorModel.ts | 34 ++++++++----------- .../notebook/common/notebookService.ts | 2 +- 3 files changed, 23 insertions(+), 35 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index defd17ca549..f1c91c4bc24 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -657,43 +657,37 @@ export class NotebookService extends Disposable implements INotebookService, ICu return this.notebookRenderersInfoStore.get(id); } - async resolveNotebook(viewType: string, uri: URI, forceReload: boolean, editorId?: string, backupId?: string): Promise { + async resolveNotebook(viewType: string, uri: URI, forceReload: boolean, editorId?: string, backupId?: string): Promise { - await this.canResolve(viewType); - - const provider = this._notebookProviders.get(viewType); - if (!provider) { + if (!await this.canResolve(viewType)) { throw new Error(`CANNOT load notebook, no provider for '${viewType}'`); } - let notebookModel: NotebookTextModel | undefined = undefined; + const provider = this._notebookProviders.get(viewType)!; + let notebookModel: NotebookTextModel; if (this._models.has(uri)) { // the model already exists notebookModel = this._models.get(uri)!.model; if (forceReload) { await provider.controller.reloadNotebook(notebookModel); } - return notebookModel; + } else { notebookModel = this._instantiationService.createInstance(NotebookTextModel, NotebookService.mainthreadNotebookDocumentHandle++, viewType, provider.controller.supportBackup, uri); await provider.controller.createNotebook(notebookModel, backupId); - - if (!notebookModel) { - return undefined; - } } // new notebook model created const modelData = new ModelData( - notebookModel!, + notebookModel, (model) => this._onWillDisposeDocument(model), ); this._models.set(uri, modelData); - this._onNotebookDocumentAdd.fire([notebookModel!.uri]); + this._onNotebookDocumentAdd.fire([notebookModel.uri]); // after the document is added to the store and sent to ext host, we transform the ouputs - await this.transformTextModelOutputs(notebookModel!); + await this.transformTextModelOutputs(notebookModel); if (editorId) { await provider.controller.resolveNotebookEditor(viewType, uri, editorId); diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts index 472ba0217a6..2c750fafbce 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts @@ -11,12 +11,12 @@ import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/no import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { URI } from 'vs/base/common/uri'; import { IWorkingCopyService, IWorkingCopy, IWorkingCopyBackup, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; -import { basename } from 'vs/base/common/resources'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { Schemas } from 'vs/base/common/network'; import { IFileStatWithMetadata, IFileService } from 'vs/platform/files/common/files'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { ILabelService } from 'vs/platform/label/common/label'; export interface INotebookLoadOptions { @@ -29,7 +29,7 @@ export interface INotebookLoadOptions { } -export class NotebookEditorModel extends EditorModel implements IWorkingCopy, INotebookEditorModel { +export class NotebookEditorModel extends EditorModel implements INotebookEditorModel { protected readonly _onDidChangeDirty = this._register(new Emitter()); readonly onDidChangeDirty = this._onDidChangeDirty.event; private readonly _onDidChangeContent = this._register(new Emitter()); @@ -41,31 +41,29 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN return this._notebook; } - private _name!: string; - - get name() { - return this._name; - } - - private _workingCopyResource: URI; + private readonly _name: string; + private readonly _workingCopyResource: URI; constructor( - public readonly resource: URI, - public readonly viewType: string, + readonly resource: URI, + readonly viewType: string, @INotebookService private readonly _notebookService: INotebookService, @IWorkingCopyService private readonly _workingCopyService: IWorkingCopyService, @IBackupFileService private readonly _backupFileService: IBackupFileService, @IFileService private readonly _fileService: IFileService, - @INotificationService private readonly _notificationService: INotificationService + @INotificationService private readonly _notificationService: INotificationService, + @ILabelService labelService: ILabelService, ) { super(); + this._name = labelService.getUriBasenameLabel(resource); + const input = this; this._workingCopyResource = resource.with({ scheme: Schemas.vscodeNotebook }); const workingCopyAdapter = new class implements IWorkingCopy { readonly resource = input._workingCopyResource; - get name() { return input.name; } - readonly capabilities = input.isUntitled() ? WorkingCopyCapabilities.Untitled : input.capabilities; + get name() { return input._name; } + readonly capabilities = input.isUntitled() ? WorkingCopyCapabilities.Untitled : WorkingCopyCapabilities.None; readonly onDidChangeDirty = input.onDidChangeDirty; readonly onDidChangeContent = input.onDidChangeContent; isDirty(): boolean { return input.isDirty(); } @@ -77,8 +75,6 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN this._register(this._workingCopyService.registerWorkingCopy(workingCopyAdapter)); } - capabilities = WorkingCopyCapabilities.None; - async backup(): Promise> { if (this._notebook.supportBackup) { const tokenSource = new CancellationTokenSource(); @@ -138,15 +134,13 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN } private async _loadFromProvider(forceReloadFromDisk: boolean, editorId: string | undefined, backupId: string | undefined) { - const notebook = await this._notebookService.resolveNotebook(this.viewType!, this.resource, forceReloadFromDisk, editorId, backupId); - this._notebook = notebook!; + this._notebook = await this._notebookService.resolveNotebook(this.viewType!, this.resource, forceReloadFromDisk, editorId, backupId); + const newStats = await this._resolveStats(this.resource); this._lastResolvedFileStat = newStats; this._register(this._notebook); - this._name = basename(this._notebook!.uri); - this._register(this._notebook.onDidChangeContent(() => { this._onDidChangeContent.fire(); })); diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index f9acd633254..3d560795602 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -58,7 +58,7 @@ export interface INotebookService { getContributedNotebookOutputRenderers(id: string): NotebookOutputRendererInfo | undefined; getRendererInfo(id: string): INotebookRendererInfo | undefined; - resolveNotebook(viewType: string, uri: URI, forceReload: boolean, editorId?: string, backupId?: string): Promise; + resolveNotebook(viewType: string, uri: URI, forceReload: boolean, editorId?: string, backupId?: string): Promise; getNotebookTextModel(uri: URI): NotebookTextModel | undefined; getNotebookTextModels(): Iterable; executeNotebook(viewType: string, uri: URI, kernelId: string): Promise; From 779436d9813e9568308893f371493a38e5ebf390 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 31 Aug 2020 17:22:52 +0200 Subject: [PATCH 721/736] fix another cycling types reference --- src/vs/workbench/api/common/extHostDecorations.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHostDecorations.ts b/src/vs/workbench/api/common/extHostDecorations.ts index d50d8f15dd2..3e52bbcc3aa 100644 --- a/src/vs/workbench/api/common/extHostDecorations.ts +++ b/src/vs/workbench/api/common/extHostDecorations.ts @@ -19,7 +19,7 @@ interface ProviderData { extensionId: ExtensionIdentifier; } -export class ExtHostDecorations implements IExtHostDecorations { +export class ExtHostDecorations implements ExtHostDecorationsShape { private static _handlePool = 0; @@ -85,4 +85,4 @@ export class ExtHostDecorations implements IExtHostDecorations { } export const IExtHostDecorations = createDecorator('IExtHostDecorations'); -export interface IExtHostDecorations extends ExtHostDecorations, ExtHostDecorationsShape { } +export interface IExtHostDecorations extends ExtHostDecorations { } From 2e8152ce75e2b0012812d29f585fdd8a2088fc29 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 31 Aug 2020 17:41:20 +0200 Subject: [PATCH 722/736] Get authentication session info from workbench html in dev mode --- .../code/browser/workbench/workbench-dev.html | 5 +- .../services/userData/browser/userDataInit.ts | 46 ++++++++++++++----- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/vs/code/browser/workbench/workbench-dev.html b/src/vs/code/browser/workbench/workbench-dev.html index 55ae7a39871..aeb8c5e1e66 100644 --- a/src/vs/code/browser/workbench/workbench-dev.html +++ b/src/vs/code/browser/workbench/workbench-dev.html @@ -14,8 +14,11 @@ - + + + + diff --git a/src/vs/workbench/services/userData/browser/userDataInit.ts b/src/vs/workbench/services/userData/browser/userDataInit.ts index d3fc176f380..569affa26a9 100644 --- a/src/vs/workbench/services/userData/browser/userDataInit.ts +++ b/src/vs/workbench/services/userData/browser/userDataInit.ts @@ -19,7 +19,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IRequestService } from 'vs/platform/request/common/request'; import { CONFIGURATION_SYNC_STORE_KEY, IUserDataSyncStoreClient, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; import { URI } from 'vs/base/common/uri'; -import { getCurrentAuthenticationSessionInfo } from 'vs/workbench/services/authentication/browser/authenticationService'; +import { AuthenticationSessionInfo, getCurrentAuthenticationSessionInfo } from 'vs/workbench/services/authentication/browser/authenticationService'; import { getSyncAreaLabel } from 'vs/workbench/services/userDataSync/common/userDataSync'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -80,18 +80,13 @@ export class UserDataInitializationService implements IUserDataInitializationSer return; } - if (!this.environmentService.options?.credentialsProvider) { - this.logService.trace(`Skipping initializing user data as credentials provider is not provided`); - return; - } + const authenticationSession = await this.getCurrentAuthenticationSessionInfo(); - let authenticationSession; - try { - authenticationSession = await getCurrentAuthenticationSessionInfo(this.environmentService, this.productService); - } catch (error) { - this.logService.error(error); - } if (!authenticationSession) { + if (!this.environmentService.options?.credentialsProvider) { + this.logService.trace(`Skipping initializing user data as credentials provider is not provided`); + return; + } this.logService.trace(`Skipping initializing user data as authentication session is not set`); return; } @@ -105,6 +100,35 @@ export class UserDataInitializationService implements IUserDataInitializationSer return this._userDataSyncStoreClientPromise; } + private async getCurrentAuthenticationSessionInfo(): Promise { + if (this.environmentService.options?.credentialsProvider) { + try { + const currentAuthenticationSessionInfo = await getCurrentAuthenticationSessionInfo(this.environmentService, this.productService); + if (currentAuthenticationSessionInfo) { + return currentAuthenticationSessionInfo; + } + } catch (error) { + this.logService.error(error); + return undefined; + } + } + + if (!this.environmentService.isBuilt) { + const authenticationSessionInfoElement = document.getElementById('vscode-workbench-authentication-session'); + const authenticationSessionInfoElementAttribute = authenticationSessionInfoElement ? authenticationSessionInfoElement.getAttribute('data-settings') : undefined; + if (authenticationSessionInfoElementAttribute) { + try { + return JSON.parse(authenticationSessionInfoElementAttribute); + } catch (error) { + this.logService.error(error); + return undefined; + } + } + } + + return undefined; + } + async initializeRequiredResources(): Promise { return this.initialize([SyncResource.Settings, SyncResource.GlobalState]); } From 94b05d07cb2891f7d42f51c217ae2ffb92e892eb Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 31 Aug 2020 17:47:25 +0200 Subject: [PATCH 723/736] Remove some globals --- .../services/extensions/worker/extensionHostWorker.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts index 2041723074a..c1e85e6539f 100644 --- a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts +++ b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts @@ -36,6 +36,15 @@ self.postMessage = () => console.trace(`'postMessage' has been blocked`); const nativeAddEventLister = addEventListener.bind(self); self.addEventLister = () => console.trace(`'addEventListener' has been blocked`); +(self)['AMDLoader'] = undefined; +(self)['NLSLoaderPlugin'] = undefined; +(self)['define'] = undefined; +(self)['require'] = undefined; +(self)['webkitRequestFileSystem'] = undefined; +(self)['webkitRequestFileSystemSync'] = undefined; +(self)['webkitResolveLocalFileSystemSyncURL'] = undefined; +(self)['webkitResolveLocalFileSystemURL'] = undefined; + if (location.protocol === 'data:') { // make sure new Worker(...) always uses data: const _Worker = Worker; From 0090101fa8c2f6adc7ba3b6189d05c89a70ce5d7 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Mon, 31 Aug 2020 13:00:26 -0400 Subject: [PATCH 724/736] Bumps version of github-browser --- product.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product.json b/product.json index 7dcb54372e5..640b9100707 100644 --- a/product.json +++ b/product.json @@ -123,7 +123,7 @@ "webBuiltInExtensions": [ { "name": "ms-vscode.github-browser", - "version": "0.0.3", + "version": "0.0.5", "repo": "https://github.com/Microsoft/vscode-github-browser", "metadata": { "id": "c1bcff4b-4ecb-466e-b8f6-b02788b5fb5a", From 747d667946a59a71cddd8957677b92b7dc6ef3e3 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 31 Aug 2020 20:09:23 +0200 Subject: [PATCH 725/736] add file associations for ".c++" and ".h++" file extensions. Fixes #104320 --- extensions/cpp/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/cpp/package.json b/extensions/cpp/package.json index e02009fb57f..d302aa46ffd 100644 --- a/extensions/cpp/package.json +++ b/extensions/cpp/package.json @@ -35,6 +35,7 @@ ".hpp", ".hh", ".hxx", + ".h++", ".h", ".ii", ".ino", From 7f79730cb65b316efcf87f4cfb0f44c76d9eaded Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 31 Aug 2020 20:27:27 +0200 Subject: [PATCH 726/736] Revert "Run menu does not remember dynamic launch configurations" This reverts commit d4248f3df5f486603c65aa89e4d061bf23e15e48. --- .../browser/debugConfigurationManager.ts | 28 +++++-------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index ed6e487941d..e28eaf83505 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -46,7 +46,6 @@ jsonRegistry.registerSchema(launchSchemaId, launchSchema); const DEBUG_SELECTED_CONFIG_NAME_KEY = 'debug.selectedconfigname'; const DEBUG_SELECTED_ROOT = 'debug.selectedroot'; -const DEBUG_SELECTED_DYNAMIC_CONFIG = 'debug.selecteddynamicconfig'; interface IDynamicPickItem { label: string, launch: ILaunch, config: IConfig } @@ -84,30 +83,23 @@ export class ConfigurationManager implements IConfigurationManager { this.toDispose = []; this.initLaunches(); this.registerListeners(); + const previousSelectedRoot = this.storageService.get(DEBUG_SELECTED_ROOT, StorageScope.WORKSPACE); + const previousSelectedLaunch = this.launches.find(l => l.uri.toString() === previousSelectedRoot); this.debugConfigurationTypeContext = CONTEXT_DEBUG_CONFIGURATION_TYPE.bindTo(contextKeyService); this.debuggersAvailable = CONTEXT_DEBUGGERS_AVAILABLE.bindTo(contextKeyService); - + if (previousSelectedLaunch && previousSelectedLaunch.getConfigurationNames().length) { + this.selectConfiguration(previousSelectedLaunch, this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE)); + } else if (this.launches.length > 0) { + this.selectConfiguration(undefined); + } } // debuggers registerDebugAdapterFactory(debugTypes: string[], debugAdapterLauncher: IDebugAdapterFactory): IDisposable { - const firstRegistration = this.debugAdapterFactories.size === 0; debugTypes.forEach(debugType => this.debugAdapterFactories.set(debugType, debugAdapterLauncher)); this.debuggersAvailable.set(this.debugAdapterFactories.size > 0); this._onDidRegisterDebugger.fire(); - if (firstRegistration) { - const previousSelectedRoot = this.storageService.get(DEBUG_SELECTED_ROOT, StorageScope.WORKSPACE); - const previousSelectedLaunch = this.launches.find(l => l.uri.toString() === previousSelectedRoot); - if (previousSelectedLaunch && previousSelectedLaunch.getConfigurationNames().length) { - const name = this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE); - const configStr = this.storageService.get(DEBUG_SELECTED_DYNAMIC_CONFIG, StorageScope.WORKSPACE); - const config = configStr ? JSON.parse(configStr) : undefined; - this.selectConfiguration(previousSelectedLaunch, name, config); - } else if (this.launches.length > 0) { - this.selectConfiguration(undefined); - } - } return { dispose: () => { @@ -542,12 +534,6 @@ export class ConfigurationManager implements IConfigurationManager { } this.selectedConfig = config; - if (config) { - // Only dynamic configurations get passed in the selectConfiguration. We should store them #96293 - this.storageService.store(DEBUG_SELECTED_DYNAMIC_CONFIG, JSON.stringify(config), StorageScope.WORKSPACE); - } else { - this.storageService.remove(DEBUG_SELECTED_DYNAMIC_CONFIG, StorageScope.WORKSPACE); - } const configForType = this.selectedConfig || (this.selectedLaunch && this.selectedName ? this.selectedLaunch.getConfiguration(this.selectedName) : undefined); if (configForType) { this.debugConfigurationTypeContext.set(configForType.type); From 3bb3e7ae75bc3e6013a38f4eec2c5431d6668ea5 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Mon, 31 Aug 2020 11:37:43 -0700 Subject: [PATCH 727/736] Update ThirdPartyNotices.txt --- ThirdPartyNotices.txt | 2 +- package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index ac17c9eaa64..4cf2d042f80 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -33,7 +33,7 @@ This project incorporates components from the projects listed below. The origina 26. Jxck/assert version 1.0.0 (https://github.com/Jxck/assert) 27. language-docker (https://github.com/moby/moby) 28. language-less version 0.34.2 (https://github.com/atom/language-less) -29. language-php version 0.44.4 (https://github.com/atom/language-php) +29. language-php version 0.44.5 (https://github.com/atom/language-php) 30. language-rust version 0.4.12 (https://github.com/zargony/atom-language-rust) 31. MagicStack/MagicPython version 1.1.1 (https://github.com/MagicStack/MagicPython) 32. marked version 0.6.2 (https://github.com/markedjs/marked) diff --git a/package.json b/package.json index 60fa95f8e82..a90ba51a693 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.49.0", - "distro": "67d5dac2cd17a3fa570823005d6bc33cbc67b7b5", + "distro": "bcdce5838f1e576a4d23e2ad8907828f1dbd2121", "author": { "name": "Microsoft Corporation" }, @@ -191,4 +191,4 @@ "windows-mutex": "0.3.0", "windows-process-tree": "0.2.4" } -} \ No newline at end of file +} From 774e868b259cc6523c68c316d5e4b6bc517a3796 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 31 Aug 2020 11:41:45 -0700 Subject: [PATCH 728/736] fix #105376. --- src/vs/vscode.proposed.d.ts | 5 +- .../workbench/api/common/extHostNotebook.ts | 3 +- .../api/common/extHostTypeConverters.ts | 51 +++++++++++++++++++ .../contrib/notebook/common/notebookCommon.ts | 34 ++++++++++--- 4 files changed, 82 insertions(+), 11 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 1d202abf7f3..cf59d80bc25 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1657,9 +1657,8 @@ declare module 'vscode' { } export interface NotebookDocumentFilter { - viewType?: string; - filenamePattern?: GlobPattern; - excludeFileNamePattern?: GlobPattern; + viewType?: string | string[]; + filenamePattern?: GlobPattern | { include: GlobPattern; exclude: GlobPattern }; } export interface NotebookKernelProvider { diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 16962e50bd4..15e40912036 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -1035,8 +1035,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN this._notebookKernelProviders.set(handle, adapter); this._proxy.$registerNotebookKernelProvider({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, handle, { viewType: selector.viewType, - filenamePattern: selector.filenamePattern ? typeConverters.GlobPattern.from(selector.filenamePattern) : undefined, - excludeFileNamePattern: selector.excludeFileNamePattern ? typeConverters.GlobPattern.from(selector.excludeFileNamePattern) : undefined, + filenamePattern: typeConverters.NotebookExclusiveDocumentPattern.from(selector.filenamePattern) }); return new extHostTypes.Disposable(() => { diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index b27490d45a1..18c431d0f83 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1292,3 +1292,54 @@ export namespace LogLevel { } } } + +export namespace NotebookExclusiveDocumentPattern { + export function from(pattern: { include: vscode.GlobPattern | undefined, exclude: vscode.GlobPattern | undefined }): { include: string | types.RelativePattern | undefined, exclude: string | types.RelativePattern | undefined }; + export function from(pattern: vscode.GlobPattern): string | types.RelativePattern; + export function from(pattern: undefined): undefined; + export function from(pattern: { include: vscode.GlobPattern | undefined | null, exclude: vscode.GlobPattern | undefined } | vscode.GlobPattern | undefined): string | types.RelativePattern | { include: string | types.RelativePattern | undefined, exclude: string | types.RelativePattern | undefined } | undefined; + export function from(pattern: { include: vscode.GlobPattern | undefined | null, exclude: vscode.GlobPattern | undefined } | vscode.GlobPattern | undefined): string | types.RelativePattern | { include: string | types.RelativePattern | undefined, exclude: string | types.RelativePattern | undefined } | undefined { + if (pattern instanceof types.RelativePattern) { + return pattern; + } + + if (typeof pattern === 'string') { + return pattern; + } + + + if (isRelativePattern(pattern)) { + return new types.RelativePattern(pattern.base, pattern.pattern); + } + + if (isExclusivePattern(pattern)) { + return { + include: GlobPattern.from(pattern.include) || undefined, + exclude: GlobPattern.from(pattern.exclude) || undefined + }; + } + + return undefined; // preserve `undefined` + + } + + function isExclusivePattern(obj: any): obj is { include: types.RelativePattern | undefined | null, exclude: types.RelativePattern | undefined | null } { + const ep = obj as { include: vscode.GlobPattern, exclude: vscode.GlobPattern }; + const include = GlobPattern.from(ep.include); + if (!(include && include instanceof types.RelativePattern || typeof include === 'string')) { + return false; + } + + const exclude = GlobPattern.from(ep.exclude); + if (!(exclude && exclude instanceof types.RelativePattern || typeof exclude === 'string')) { + return false; + } + + return true; + } + + function isRelativePattern(obj: any): obj is vscode.RelativePattern { + const rp = obj as vscode.RelativePattern; + return rp && typeof rp.base === 'string' && typeof rp.pattern === 'string'; + } +} diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 2cf77f6ce69..b55dc330f3c 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -729,22 +729,44 @@ export interface INotebookSearchOptions { wordSeparators?: string; } +export interface INotebookExclusiveDocumentFilter { + include?: string | glob.IRelativePattern; + exclude?: string | glob.IRelativePattern; +} + export interface INotebookDocumentFilter { - viewType?: string; - filenamePattern?: string | glob.IRelativePattern; - excludeFileNamePattern?: string | glob.IRelativePattern; + viewType?: string | string[]; + filenamePattern?: string | glob.IRelativePattern | INotebookExclusiveDocumentFilter; } //TODO@rebornix test + +function isDocumentExcludePattern(filenamePattern: string | glob.IRelativePattern | INotebookExclusiveDocumentFilter): filenamePattern is { include: string | glob.IRelativePattern; exclude: string | glob.IRelativePattern; } { + const arg = filenamePattern as INotebookExclusiveDocumentFilter; + + if ((typeof arg.include === 'string' || glob.isRelativePattern(arg.include)) + && (typeof arg.exclude === 'string' || glob.isRelativePattern(arg.exclude))) { + return true; + } + + return false; +} export function notebookDocumentFilterMatch(filter: INotebookDocumentFilter, viewType: string, resource: URI): boolean { + if (Array.isArray(filter.viewType) && filter.viewType.indexOf(viewType) >= 0) { + return true; + } + if (filter.viewType === viewType) { return true; } if (filter.filenamePattern) { - if (glob.match(filter.filenamePattern, basename(resource.fsPath).toLowerCase())) { - if (filter.excludeFileNamePattern) { - if (glob.match(filter.excludeFileNamePattern, basename(resource.fsPath).toLowerCase())) { + let filenamePattern = isDocumentExcludePattern(filter.filenamePattern) ? filter.filenamePattern.include : (filter.filenamePattern as string | glob.IRelativePattern); + let excludeFilenamePattern = isDocumentExcludePattern(filter.filenamePattern) ? filter.filenamePattern.exclude : undefined; + + if (glob.match(filenamePattern, basename(resource.fsPath).toLowerCase())) { + if (excludeFilenamePattern) { + if (glob.match(excludeFilenamePattern, basename(resource.fsPath).toLowerCase())) { // should exclude return false; From 2518d2c44d1d43ea5243ea89dd2e43f7f218ee16 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 31 Aug 2020 11:49:19 -0700 Subject: [PATCH 729/736] update kernel per document. --- src/vs/vscode.proposed.d.ts | 2 +- src/vs/workbench/api/browser/mainThreadNotebook.ts | 8 ++++---- src/vs/workbench/api/common/extHost.protocol.ts | 2 +- src/vs/workbench/api/common/extHostNotebook.ts | 5 +++-- .../notebook/browser/notebookEditorWidget.ts | 6 +++++- .../notebook/browser/notebookServiceImpl.ts | 14 +++++++------- .../contrib/notebook/common/notebookCommon.ts | 2 +- .../contrib/notebook/common/notebookService.ts | 2 +- 8 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index cf59d80bc25..1d4a9cfe523 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1662,7 +1662,7 @@ declare module 'vscode' { } export interface NotebookKernelProvider { - onDidChangeKernels?: Event; + onDidChangeKernels?: Event; provideKernels(document: NotebookDocument, token: CancellationToken): ProviderResult; resolveKernel?(kernel: T, document: NotebookDocument, webview: NotebookCommunication, token: CancellationToken): ProviderResult; } diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 0654a82df22..e076f367810 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -133,7 +133,7 @@ class DocumentAndEditorState { export class MainThreadNotebooks extends Disposable implements MainThreadNotebookShape { private readonly _notebookProviders = new Map(); private readonly _notebookKernels = new Map(); - private readonly _notebookKernelProviders = new Map, provider: IDisposable }>(); + private readonly _notebookKernelProviders = new Map, provider: IDisposable }>(); private readonly _proxy: ExtHostNotebookShape; private _toDisposeOnEditorRemove = new Map(); private _currentState?: DocumentAndEditorState; @@ -504,7 +504,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo } async $registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise { - const emitter = new Emitter(); + const emitter = new Emitter(); const that = this; const provider = this._notebookService.registerNotebookKernelProvider({ providerExtensionId: extension.id.value, @@ -551,10 +551,10 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo } } - $onNotebookKernelChange(handle: number): void { + $onNotebookKernelChange(handle: number, uriComponents: UriComponents): void { const entry = this._notebookKernelProviders.get(handle); - entry?.emitter.fire(); + entry?.emitter.fire(uriComponents ? URI.revive(uriComponents) : undefined); } async $updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 13141f6127d..43cba9960ce 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -735,7 +735,7 @@ export interface MainThreadNotebookShape extends IDisposable { $registerNotebookKernel(extension: NotebookExtensionDescription, id: string, label: string, selectors: (string | IRelativePattern)[], preloads: UriComponents[]): Promise; $registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise; $unregisterNotebookKernelProvider(handle: number): Promise; - $onNotebookKernelChange(handle: number): void; + $onNotebookKernelChange(handle: number, uri: UriComponents | undefined): void; $unregisterNotebookKernel(id: string): Promise; $tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[]): Promise; $updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 15e40912036..bf37804e839 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -795,8 +795,9 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable { super(); if (this._provider.onDidChangeKernels) { - this._register(this._provider.onDidChangeKernels(() => { - this._proxy.$onNotebookKernelChange(this._handle); + this._register(this._provider.onDidChangeKernels((e: vscode.NotebookDocument | undefined) => { + const uri = e?.uri; + this._proxy.$onNotebookKernelChange(this._handle, uri); })); } } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 547b36d2195..55c58bf796a 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -638,7 +638,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor // we don't await for it, otherwise it will slow down the file opening this._setKernels(textModel, this._currentKernelTokenSource); - this._localStore.add(this.notebookService.onDidChangeKernels(async () => { + this._localStore.add(this.notebookService.onDidChangeKernels(async (e) => { + if (e && e.toString() !== this.textModel?.uri.toString()) { + // kernel update is not for current document. + return; + } this._currentKernelTokenSource?.cancel(); this._currentKernelTokenSource = new CancellationTokenSource(); await this._setKernels(textModel, this._currentKernelTokenSource); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index f1c91c4bc24..1d0ec0b42b9 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -254,8 +254,8 @@ export class NotebookService extends Disposable implements INotebookService, ICu private readonly _onDidChangeViewTypes = new Emitter(); onDidChangeViewTypes: Event = this._onDidChangeViewTypes.event; - private readonly _onDidChangeKernels = new Emitter(); - onDidChangeKernels: Event = this._onDidChangeKernels.event; + private readonly _onDidChangeKernels = new Emitter(); + onDidChangeKernels: Event = this._onDidChangeKernels.event; private readonly _onDidChangeNotebookActiveKernel = new Emitter<{ uri: URI, providerHandle: number | undefined, kernelId: string | undefined }>(); onDidChangeNotebookActiveKernel: Event<{ uri: URI, providerHandle: number | undefined, kernelId: string | undefined }> = this._onDidChangeNotebookActiveKernel.event; private cutItems: NotebookCellTextModel[] | undefined; @@ -558,21 +558,21 @@ export class NotebookService extends Disposable implements INotebookService, ICu registerNotebookKernel(notebook: INotebookKernelInfo): void { this._notebookKernels.set(notebook.id, notebook); - this._onDidChangeKernels.fire(); + this._onDidChangeKernels.fire(undefined); } unregisterNotebookKernel(id: string): void { this._notebookKernels.delete(id); - this._onDidChangeKernels.fire(); + this._onDidChangeKernels.fire(undefined); } registerNotebookKernelProvider(provider: INotebookKernelProvider): IDisposable { const d = this.notebookKernelProviderInfoStore.add(provider); - const kernelChangeEventListener = provider.onDidChangeKernels(() => { - this._onDidChangeKernels.fire(); + const kernelChangeEventListener = provider.onDidChangeKernels((e) => { + this._onDidChangeKernels.fire(e); }); - this._onDidChangeKernels.fire(); + this._onDidChangeKernels.fire(undefined); return toDisposable(() => { kernelChangeEventListener.dispose(); d.dispose(); diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index b55dc330f3c..05f6d59b48d 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -799,7 +799,7 @@ export interface INotebookKernelProvider { providerExtensionId: string; providerDescription?: string; selector: INotebookDocumentFilter; - onDidChangeKernels: Event; + onDidChangeKernels: Event; provideKernels(uri: URI, token: CancellationToken): Promise; resolveKernel(editorId: string, uri: UriComponents, kernelId: string, token: CancellationToken): Promise; executeNotebook(uri: URI, kernelId: string, handle: number | undefined): Promise; diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index 3d560795602..3046b93f6c1 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -44,7 +44,7 @@ export interface INotebookService { onNotebookDocumentRemove: Event; onNotebookDocumentAdd: Event; onNotebookDocumentSaved: Event; - onDidChangeKernels: Event; + onDidChangeKernels: Event; onDidChangeNotebookActiveKernel: Event<{ uri: URI, providerHandle: number | undefined, kernelId: string | undefined }>; registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController): void; unregisterNotebookProvider(viewType: string): void; From c887ae1b6da1650426dcac2221441d15389686c0 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 31 Aug 2020 12:00:21 -0700 Subject: [PATCH 730/736] remove legacy registerKernel --- src/vs/vscode.proposed.d.ts | 6 -- .../api/browser/mainThreadNotebook.ts | 36 +--------- .../workbench/api/common/extHost.api.impl.ts | 4 -- .../workbench/api/common/extHost.protocol.ts | 2 - .../workbench/api/common/extHostNotebook.ts | 15 ---- .../browser/contrib/status/editorStatus.ts | 7 +- .../notebook/browser/notebookBrowser.ts | 4 +- .../notebook/browser/notebookEditorWidget.ts | 28 +++----- .../notebook/browser/notebookServiceImpl.ts | 69 +------------------ .../contrib/notebook/common/notebookCommon.ts | 12 ---- .../notebook/common/notebookService.ts | 7 +- .../notebook/test/testNotebookEditor.ts | 4 +- 12 files changed, 19 insertions(+), 175 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 1d4a9cfe523..38bab66b2a7 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1719,12 +1719,6 @@ declare module 'vscode' { provider: NotebookKernelProvider ): Disposable; - export function registerNotebookKernel( - id: string, - selectors: GlobPattern[], - kernel: NotebookKernel - ): Disposable; - export const onDidOpenNotebookDocument: Event; export const onDidCloseNotebookDocument: Event; export const onDidSaveNotebookDocument: Event; diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index e076f367810..eb223b41070 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -6,18 +6,16 @@ import * as DOM from 'vs/base/browser/dom'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; -import { IRelativePattern } from 'vs/base/common/glob'; import { combinedDisposable, Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; -import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, DisplayOrderKey, ICellEditOperation, ICellRange, IEditor, INotebookDocumentFilter, INotebookKernelInfo, NotebookCellMetadata, NotebookCellOutputsSplice, NotebookDocumentMetadata, NOTEBOOK_DISPLAY_ORDER, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, DisplayOrderKey, ICellEditOperation, ICellRange, IEditor, INotebookDocumentFilter, NotebookCellMetadata, NotebookCellOutputsSplice, NotebookDocumentMetadata, NOTEBOOK_DISPLAY_ORDER, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, MainContext, MainThreadNotebookShape, NotebookEditorRevealType, NotebookExtensionDescription } from '../common/extHost.protocol'; @@ -132,7 +130,6 @@ class DocumentAndEditorState { @extHostNamedCustomer(MainContext.MainThreadNotebook) export class MainThreadNotebooks extends Disposable implements MainThreadNotebookShape { private readonly _notebookProviders = new Map(); - private readonly _notebookKernels = new Map(); private readonly _notebookKernelProviders = new Map, provider: IDisposable }>(); private readonly _proxy: ExtHostNotebookShape; private _toDisposeOnEditorRemove = new Map(); @@ -490,19 +487,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo return; } - async $registerNotebookKernel(extension: NotebookExtensionDescription, id: string, label: string, selectors: (string | IRelativePattern)[], preloads: UriComponents[]): Promise { - const kernel = new MainThreadNotebookKernel(this._proxy, id, label, selectors, extension.id, URI.revive(extension.location), preloads.map(preload => URI.revive(preload)), this.logService); - this._notebookKernels.set(id, kernel); - this._notebookService.registerNotebookKernel(kernel); - return; - } - - async $unregisterNotebookKernel(id: string): Promise { - this._notebookKernels.delete(id); - this._notebookService.unregisterNotebookKernel(id); - return; - } - async $registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise { const emitter = new Emitter(); const that = this; @@ -657,21 +641,3 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo } } -export class MainThreadNotebookKernel implements INotebookKernelInfo { - constructor( - private readonly _proxy: ExtHostNotebookShape, - readonly id: string, - readonly label: string, - readonly selectors: (string | IRelativePattern)[], - readonly extension: ExtensionIdentifier, - readonly extensionLocation: URI, - readonly preloads: URI[], - readonly logService: ILogService - ) { - } - - async executeNotebook(viewType: string, uri: URI, handle: number | undefined): Promise { - this.logService.debug('MainThreadNotebookKernel#executeNotebook', uri.path, handle); - return this._proxy.$executeNotebook2(this.id, viewType, uri, handle); - } -} diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 0b1649905eb..3595cd3e381 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -957,10 +957,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension); return extHostNotebook.registerNotebookContentProvider(extension, viewType, provider, options); }, - registerNotebookKernel: (id: string, selector: vscode.GlobPattern[], kernel: vscode.NotebookKernel) => { - checkProposedApiEnabled(extension); - return extHostNotebook.registerNotebookKernel(extension, id, selector, kernel); - }, registerNotebookKernelProvider: (selector: vscode.NotebookDocumentFilter, provider: vscode.NotebookKernelProvider) => { checkProposedApiEnabled(extension); return extHostNotebook.registerNotebookKernelProvider(extension, selector, provider); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 43cba9960ce..4b794666295 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -732,11 +732,9 @@ export interface MainThreadNotebookShape extends IDisposable { $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, supportBackup: boolean, options: { transientOutputs: boolean; transientMetadata: TransientMetadata }): Promise; $onNotebookChange(viewType: string, resource: UriComponents): Promise; $unregisterNotebookProvider(viewType: string): Promise; - $registerNotebookKernel(extension: NotebookExtensionDescription, id: string, label: string, selectors: (string | IRelativePattern)[], preloads: UriComponents[]): Promise; $registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise; $unregisterNotebookKernelProvider(handle: number): Promise; $onNotebookKernelChange(handle: number, uri: UriComponents | undefined): void; - $unregisterNotebookKernel(id: string): Promise; $tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[]): Promise; $updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise; $updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata): Promise; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index bf37804e839..78944a62c23 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -1078,21 +1078,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN }); } - registerNotebookKernel(extension: IExtensionDescription, id: string, selectors: vscode.GlobPattern[], kernel: vscode.NotebookKernel): vscode.Disposable { - if (this._notebookKernels.has(id)) { - throw new Error(`Notebook kernel for '${id}' already registered`); - } - - this._notebookKernels.set(id, { kernel, extension }); - const transformedSelectors = selectors.map(selector => typeConverters.GlobPattern.from(selector)); - - this._proxy.$registerNotebookKernel({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, id, kernel.label, transformedSelectors, kernel.preloads || []); - return new extHostTypes.Disposable(() => { - this._notebookKernels.delete(id); - this._proxy.$unregisterNotebookKernel(id); - }); - } - async $resolveNotebookData(viewType: string, uri: UriComponents, backupId?: string): Promise { const provider = this._notebookContentProviders.get(viewType); const revivedUri = URI.revive(uri); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts b/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts index dca894ed1b9..8227a956f7e 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts @@ -14,7 +14,7 @@ import { INotebookEditor, NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; -import { INotebookKernelInfo2, INotebookKernelInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookKernelInfo2 } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; @@ -50,8 +50,7 @@ registerAction2(class extends Action2 { const tokenSource = new CancellationTokenSource(); const availableKernels2 = await notebookService.getContributedNotebookKernels2(editor.viewModel!.viewType, editor.viewModel!.uri, tokenSource.token); - const availableKernels = notebookService.getContributedNotebookKernels(editor.viewModel!.viewType, editor.viewModel!.uri); - const picks: QuickPickInput[] = [...availableKernels2, ...availableKernels].map((a) => { + const picks: QuickPickInput[] = [...availableKernels2].map((a) => { return { id: a.id, label: a.label, @@ -171,7 +170,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution { } } - showKernelStatus(kernel: INotebookKernelInfo | INotebookKernelInfo2 | undefined) { + showKernelStatus(kernel: INotebookKernelInfo2 | undefined) { this.kernelInfoElement.value = this._statusbarService.addEntry({ text: kernel ? kernel.label : 'Choose Kernel', ariaLabel: kernel ? kernel.label : 'Choose Kernel', diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 6d6685bba57..889546ea69e 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -22,7 +22,7 @@ import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/outpu import { RunStateRenderer, TimerRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer'; import { CellViewModel, IModelDecorationsChangeAccessor, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { CellKind, IProcessedOutput, IRenderOutput, NotebookCellMetadata, NotebookDocumentMetadata, INotebookKernelInfo, IEditor, INotebookKernelInfo2, IInsetRenderOutput, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, IProcessedOutput, IRenderOutput, NotebookCellMetadata, NotebookDocumentMetadata, IEditor, INotebookKernelInfo2, IInsetRenderOutput, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { IMenu } from 'vs/platform/actions/common/actions'; @@ -218,7 +218,7 @@ export interface INotebookEditor extends IEditor { readonly onDidChangeModel: Event; readonly onDidFocusEditorWidget: Event; readonly isNotebookEditor: boolean; - activeKernel: INotebookKernelInfo | INotebookKernelInfo2 | undefined; + activeKernel: INotebookKernelInfo2 | undefined; multipleKernelsAvailable: boolean; readonly onDidChangeAvailableKernels: Event; readonly onDidChangeKernel: Event; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 55c58bf796a..4d41667716d 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -52,7 +52,7 @@ import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewMod import { NotebookEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { CellViewModel, IModelDecorationsChangeAccessor, INotebookEditorViewState, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellKind, CellToolbarLocKey, ICellRange, IInsetRenderOutput, INotebookKernelInfo, INotebookKernelInfo2, INotebookKernelInfoDto, IProcessedOutput, isTransformedDisplayOutput, NotebookCellRunState, NotebookRunState, ShowCellStatusbarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellToolbarLocKey, ICellRange, IInsetRenderOutput, INotebookKernelInfo2, INotebookKernelInfoDto, IProcessedOutput, isTransformedDisplayOutput, NotebookCellRunState, NotebookRunState, ShowCellStatusbarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { editorGutterModifiedBackground } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; @@ -144,7 +144,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return this._notebookViewModel?.notebookDocument; } - private _activeKernel: INotebookKernelInfo | INotebookKernelInfo2 | undefined = undefined; + private _activeKernel: INotebookKernelInfo2 | undefined = undefined; private readonly _onDidChangeKernel = this._register(new Emitter()); readonly onDidChangeKernel: Event = this._onDidChangeKernel.event; private readonly _onDidChangeAvailableKernels = this._register(new Emitter()); @@ -154,7 +154,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return this._activeKernel; } - set activeKernel(kernel: INotebookKernelInfo | INotebookKernelInfo2 | undefined) { + set activeKernel(kernel: INotebookKernelInfo2 | undefined) { if (this._isDisposed) { return; } @@ -712,13 +712,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return; } - const availableKernels = this.notebookService.getContributedNotebookKernels(textModel.viewType, textModel.uri); - if (tokenSource.token.isCancellationRequested) { return; } - if ((availableKernels.length + availableKernels2.length) > 1) { + if ((availableKernels2.length) > 1) { this._notebookHasMultipleKernels!.set(true); this.multipleKernelsAvailable = true; } else { @@ -726,7 +724,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this.multipleKernelsAvailable = false; } - const activeKernelStillExist = [...availableKernels2, ...availableKernels].find(kernel => kernel.id === this.activeKernel?.id && this.activeKernel?.id !== undefined); + const activeKernelStillExist = [...availableKernels2].find(kernel => kernel.id === this.activeKernel?.id && this.activeKernel?.id !== undefined); if (activeKernelStillExist) { // the kernel still exist, we don't want to modify the selection otherwise user's temporary preference is lost @@ -738,10 +736,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } // the provider doesn't have a builtin kernel, choose a kernel - this.activeKernel = availableKernels[0]; - if (this.activeKernel) { - await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel); - } + // this.activeKernel = availableKernels[0]; + // if (this.activeKernel) { + // await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel); + // } tokenSource.dispose(); } @@ -1456,8 +1454,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } await (this._activeKernel as INotebookKernelInfo2).executeNotebookCell!(this._notebookViewModel!.uri, undefined); - } else { - await this.notebookService.executeNotebook(this._notebookViewModel!.viewType, this._notebookViewModel!.uri, this._activeKernel.id); } } } @@ -1502,16 +1498,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private async _executeNotebookCell(cell: ICellViewModel): Promise { const provider = this.notebookService.getContributedNotebookProviders(this.viewModel!.uri)[0]; if (provider) { - const viewType = provider.id; - const notebookUri = this._notebookViewModel!.uri; - if (this._activeKernel) { // TODO@rebornix temp any cast, should be removed once we remove legacy kernel support if ((this._activeKernel as INotebookKernelInfo2).executeNotebookCell) { await (this._activeKernel as INotebookKernelInfo2).executeNotebookCell!(this._notebookViewModel!.uri, cell.handle); - } else { - - return await this.notebookService.executeNotebookCell(viewType, notebookUri, cell.handle, this._activeKernel.id); } } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 1d0ec0b42b9..74881636cc3 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -6,11 +6,9 @@ import { flatten } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; -import * as glob from 'vs/base/common/glob'; import { Iterable } from 'vs/base/common/iterator'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ResourceMap } from 'vs/base/common/map'; -import { basename } from 'vs/base/common/path'; import { URI } from 'vs/base/common/uri'; import { RedoCommand, UndoCommand } from 'vs/editor/browser/editorExtensions'; import { CopyAction, CutAction, PasteAction } from 'vs/editor/contrib/clipboard/clipboard'; @@ -28,7 +26,7 @@ import { NotebookKernelProviderAssociationRegistry, NotebookViewTypesExtensionRe import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellEditType, CellOutputKind, CellUri, DisplayOrderKey, ICellEditOperation, IDisplayOutput, INotebookKernelInfo, INotebookKernelInfo2, INotebookKernelProvider, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, NotebookCellOutputsSplice, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellEditType, CellOutputKind, CellUri, DisplayOrderKey, ICellEditOperation, IDisplayOutput, INotebookKernelInfo2, INotebookKernelProvider, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, NotebookCellOutputsSplice, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer'; import { NotebookEditorDescriptor, NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; @@ -229,7 +227,6 @@ export class NotebookService extends Disposable implements INotebookService, ICu declare readonly _serviceBrand: undefined; static mainthreadNotebookDocumentHandle: number = 0; private readonly _notebookProviders = new Map(); - private readonly _notebookKernels = new Map(); notebookProviderInfoStore: NotebookProviderInfoStore; notebookRenderersInfoStore: NotebookOutputRendererInfoStore = new NotebookOutputRendererInfoStore(); notebookKernelProviderInfoStore: NotebookKernelProviderInfoStore = new NotebookKernelProviderInfoStore(); @@ -556,16 +553,6 @@ export class NotebookService extends Disposable implements INotebookService, ICu this._onDidChangeViewTypes.fire(); } - registerNotebookKernel(notebook: INotebookKernelInfo): void { - this._notebookKernels.set(notebook.id, notebook); - this._onDidChangeKernels.fire(undefined); - } - - unregisterNotebookKernel(id: string): void { - this._notebookKernels.delete(id); - this._onDidChangeKernels.fire(undefined); - } - registerNotebookKernelProvider(provider: INotebookKernelProvider): IDisposable { const d = this.notebookKernelProviderInfoStore.add(provider); const kernelChangeEventListener = provider.onDidChangeKernels((e) => { @@ -613,46 +600,6 @@ export class NotebookService extends Disposable implements INotebookService, ICu return flatten(result); } - getContributedNotebookKernels(viewType: string, resource: URI): INotebookKernelInfo[] { - let kernelInfos: INotebookKernelInfo[] = []; - this._notebookKernels.forEach(kernel => { - if (this._notebookKernelMatch(resource, kernel!.selectors)) { - kernelInfos.push(kernel!); - } - }); - - // sort by extensions - - const notebookContentProvider = this._notebookProviders.get(viewType); - - if (!notebookContentProvider) { - return kernelInfos; - } - - kernelInfos = kernelInfos.sort((a, b) => { - if (a.extension.value === notebookContentProvider!.extensionData.id.value) { - return -1; - } else if (b.extension.value === notebookContentProvider!.extensionData.id.value) { - return 1; - } else { - return 0; - } - }); - - return kernelInfos; - } - - private _notebookKernelMatch(resource: URI, selectors: (string | glob.IRelativePattern)[]): boolean { - for (let i = 0; i < selectors.length; i++) { - const pattern = typeof selectors[i] !== 'string' ? selectors[i] : selectors[i].toString(); - if (glob.match(pattern, basename(resource.fsPath).toLowerCase())) { - return true; - } - } - - return false; - } - getRendererInfo(id: string): INotebookRendererInfo | undefined { return this.notebookRenderersInfoStore.get(id); } @@ -817,20 +764,6 @@ export class NotebookService extends Disposable implements INotebookService, ICu return this.notebookRenderersInfoStore.getContributedRenderer(mimeType); } - async executeNotebook(viewType: string, uri: URI, kernelId: string): Promise { - const kernel = this._notebookKernels.get(kernelId); - if (kernel) { - await kernel.executeNotebook(viewType, uri, undefined); - } - } - - async executeNotebookCell(viewType: string, uri: URI, handle: number, kernelId: string): Promise { - const kernel = this._notebookKernels.get(kernelId); - if (kernel) { - await kernel.executeNotebook(viewType, uri, handle); - } - } - getContributedNotebookProviders(resource: URI): readonly NotebookProviderInfo[] { return this.notebookProviderInfoStore.getContributedNotebook(resource); } diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 05f6d59b48d..8c8c55a6fb6 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -133,18 +133,6 @@ export interface INotebookRendererInfo { matches(mimeType: string): boolean; } -export interface INotebookKernelInfo { - id: string; - label: string, - selectors: (string | glob.IRelativePattern)[], - extension: ExtensionIdentifier; - extensionLocation: URI, - preloads: URI[]; - providerHandle?: number; - executeNotebook(viewType: string, uri: URI, handle: number | undefined): Promise; - -} - export interface INotebookKernelInfoDto { id: string; label: string, diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index 3046b93f6c1..40e62b67084 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -9,7 +9,7 @@ import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/noteb import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.protocol'; import { Event } from 'vs/base/common/event'; import { - INotebookTextModel, INotebookRendererInfo, INotebookKernelInfo, + INotebookTextModel, INotebookRendererInfo, IEditor, ICellEditOperation, NotebookCellOutputsSplice, INotebookKernelProvider, INotebookKernelInfo2, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; @@ -50,10 +50,7 @@ export interface INotebookService { unregisterNotebookProvider(viewType: string): void; transformEditsOutputs(textModel: NotebookTextModel, edits: ICellEditOperation[]): void; transformSpliceOutputs(textModel: NotebookTextModel, splices: NotebookCellOutputsSplice[]): void; - registerNotebookKernel(kernel: INotebookKernelInfo): void; - unregisterNotebookKernel(id: string): void; registerNotebookKernelProvider(provider: INotebookKernelProvider): IDisposable; - getContributedNotebookKernels(viewType: string, resource: URI): readonly INotebookKernelInfo[]; getContributedNotebookKernels2(viewType: string, resource: URI, token: CancellationToken): Promise; getContributedNotebookOutputRenderers(id: string): NotebookOutputRendererInfo | undefined; getRendererInfo(id: string): INotebookRendererInfo | undefined; @@ -61,8 +58,6 @@ export interface INotebookService { resolveNotebook(viewType: string, uri: URI, forceReload: boolean, editorId?: string, backupId?: string): Promise; getNotebookTextModel(uri: URI): NotebookTextModel | undefined; getNotebookTextModels(): Iterable; - executeNotebook(viewType: string, uri: URI, kernelId: string): Promise; - executeNotebookCell(viewType: string, uri: URI, handle: number, kernelId: string): Promise; getContributedNotebookProviders(resource: URI): readonly NotebookProviderInfo[]; getContributedNotebookProvider(viewType: string): NotebookProviderInfo | undefined; getNotebookProviderResourceRoots(): URI[]; diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index 3bb3c92a63d..e21f25b2b02 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -18,7 +18,7 @@ import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/v import { CellViewModel, IModelDecorationsChangeAccessor, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellKind, CellUri, INotebookEditorModel, IProcessedOutput, NotebookCellMetadata, INotebookKernelInfo, IInsetRenderOutput, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellUri, INotebookEditorModel, IProcessedOutput, NotebookCellMetadata, IInsetRenderOutput, ICellRange, INotebookKernelInfo2 } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { ICompositeCodeEditor, IEditor } from 'vs/editor/common/editorCommon'; import { NotImplementedError } from 'vs/base/common/errors'; @@ -106,7 +106,7 @@ export class TestNotebookEditor implements INotebookEditor { } cursorNavigationMode = false; - activeKernel: INotebookKernelInfo | undefined; + activeKernel: INotebookKernelInfo2 | undefined; onDidChangeKernel: Event = new Emitter().event; onDidChangeActiveEditor: Event = new Emitter().event; activeCodeEditor: IEditor | undefined; From 311b7d1ce753e7bb8cac2e3f83e6f371945b0c40 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 31 Aug 2020 12:03:36 -0700 Subject: [PATCH 731/736] :lipstick: --- .../contrib/notebook/browser/notebookEditorWidget.ts | 4 ++-- .../contrib/notebook/common/notebookCommon.ts | 11 ----------- .../contrib/notebook/common/notebookProvider.ts | 3 +-- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 4d41667716d..727dcb36e40 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -52,7 +52,7 @@ import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewMod import { NotebookEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { CellViewModel, IModelDecorationsChangeAccessor, INotebookEditorViewState, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellKind, CellToolbarLocKey, ICellRange, IInsetRenderOutput, INotebookKernelInfo2, INotebookKernelInfoDto, IProcessedOutput, isTransformedDisplayOutput, NotebookCellRunState, NotebookRunState, ShowCellStatusbarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellToolbarLocKey, ICellRange, IInsetRenderOutput, INotebookKernelInfo2, IProcessedOutput, isTransformedDisplayOutput, NotebookCellRunState, NotebookRunState, ShowCellStatusbarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { editorGutterModifiedBackground } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; @@ -828,7 +828,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor tokenSource.dispose(); } - private async _loadKernelPreloads(extensionLocation: URI, kernel: INotebookKernelInfoDto) { + private async _loadKernelPreloads(extensionLocation: URI, kernel: INotebookKernelInfo2) { if (kernel.preloads && kernel.preloads.length) { await this._resolveWebview(); this._webview?.updateKernelPreloads([extensionLocation], kernel.preloads.map(preload => URI.revive(preload))); diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 8c8c55a6fb6..6eb35535a00 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -133,17 +133,6 @@ export interface INotebookRendererInfo { matches(mimeType: string): boolean; } -export interface INotebookKernelInfoDto { - id: string; - label: string, - extensionLocation: URI; - preloads?: UriComponents[]; -} - -export interface INotebookSelectors { - readonly filenamePattern?: string; -} - export interface IStreamOutput { outputKind: CellOutputKind.Text; text: string; diff --git a/src/vs/workbench/contrib/notebook/common/notebookProvider.ts b/src/vs/workbench/contrib/notebook/common/notebookProvider.ts index cab62ec09c9..3649b54141c 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookProvider.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookProvider.ts @@ -6,7 +6,7 @@ import * as glob from 'vs/base/common/glob'; import { URI } from 'vs/base/common/uri'; import { basename } from 'vs/base/common/path'; -import { INotebookKernelInfoDto, NotebookEditorPriority } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookEditorPriority } from 'vs/workbench/contrib/notebook/common/notebookCommon'; export interface NotebookSelector { readonly filenamePattern?: string; @@ -22,7 +22,6 @@ export interface NotebookEditorDescriptor { readonly providerDescription?: string; readonly providerDisplayName: string; readonly providerExtensionLocation: URI; - kernel?: INotebookKernelInfoDto; } export class NotebookProviderInfo implements NotebookEditorDescriptor { From 23faaa293bc98498cf8dfb8c7a880d8cd20299e1 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 31 Aug 2020 12:07:11 -0700 Subject: [PATCH 732/736] add details to notebook kernel. --- src/vs/vscode.proposed.d.ts | 1 + src/vs/workbench/api/common/extHostNotebook.ts | 1 + .../notebook/browser/contrib/status/editorStatus.ts | 9 ++++----- .../contrib/notebook/browser/notebookServiceImpl.ts | 1 + .../workbench/contrib/notebook/common/notebookCommon.ts | 1 + 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 38bab66b2a7..eeac69bcee3 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1648,6 +1648,7 @@ declare module 'vscode' { readonly id?: string; label: string; description?: string; + detail?: string; isPreferred?: boolean; preloads?: Uri[]; executeCell(document: NotebookDocument, cell: NotebookCell): void; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 78944a62c23..47714ac961a 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -829,6 +829,7 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable { extension: this._extension.identifier, extensionLocation: this._extension.extensionLocation, description: kernel.description, + detail: kernel.detail, isPreferred: kernel.isPreferred, preloads: kernel.preloads }; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts b/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts index 8227a956f7e..2c2c937e6cf 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts @@ -56,17 +56,16 @@ registerAction2(class extends Action2 { label: a.label, picked: a.id === activeKernel?.id, description: - (a as INotebookKernelInfo2).description - ? (a as INotebookKernelInfo2).description + a.description + ? a.description : a.extension.value + (a.id === activeKernel?.id ? nls.localize('currentActiveKernel', " (Currently Active)") : ''), + detail: a.detail, kernelProviderId: a.extension.value, run: async () => { editor.activeKernel = a; - if ((a as any).resolve) { - (a as INotebookKernelInfo2).resolve(editor.uri!, editor.getId(), tokenSource.token); - } + a.resolve(editor.uri!, editor.getId(), tokenSource.token); }, buttons: [{ iconClass: 'codicon-settings-gear', diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 74881636cc3..ad28bde4973 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -579,6 +579,7 @@ export class NotebookService extends Disposable implements INotebookService, ICu id: dto.id, label: dto.label, description: dto.description, + detail: dto.detail, isPreferred: dto.isPreferred, preloads: dto.preloads, providerHandle: dto.providerHandle, diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 6eb35535a00..64127dec611 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -762,6 +762,7 @@ export interface INotebookKernelInfoDto2 { extensionLocation: URI; providerHandle?: number; description?: string; + detail?: string; isPreferred?: boolean; preloads?: UriComponents[]; } From d3cfbddde1f6561c0a0cedad585abc7b635f403a Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 31 Aug 2020 12:16:35 -0700 Subject: [PATCH 733/736] fix tests. --- .../vscode-notebook-tests/src/notebookSmokeTestMain.ts | 9 ++++++++- extensions/vscode-notebook-tests/src/notebookTestMain.ts | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/extensions/vscode-notebook-tests/src/notebookSmokeTestMain.ts b/extensions/vscode-notebook-tests/src/notebookSmokeTestMain.ts index 0cec3fe06d6..10ab61306dc 100644 --- a/extensions/vscode-notebook-tests/src/notebookSmokeTestMain.ts +++ b/extensions/vscode-notebook-tests/src/notebookSmokeTestMain.ts @@ -67,8 +67,9 @@ export function smokeTestActivate(context: vscode.ExtensionContext): any { } })); - context.subscriptions.push(vscode.notebook.registerNotebookKernel('notebookSmokeTest', ['*.smoke-nb'], { + const kernel: vscode.NotebookKernel = { label: 'notebookSmokeTest', + isPreferred: true, executeAllCells: async (_document: vscode.NotebookDocument) => { for (let i = 0; i < _document.cells.length; i++) { _document.cells[i].outputs = [{ @@ -94,6 +95,12 @@ export function smokeTestActivate(context: vscode.ExtensionContext): any { return; }, cancelCellExecution: async () => { } + }; + + context.subscriptions.push(vscode.notebook.registerNotebookKernelProvider({ filenamePattern: '*.smoke-nb' }, { + provideKernels: async () => { + return [kernel]; + } })); context.subscriptions.push(vscode.commands.registerCommand('vscode-notebook-tests.debugAction', async (cell: vscode.NotebookCell) => { diff --git a/extensions/vscode-notebook-tests/src/notebookTestMain.ts b/extensions/vscode-notebook-tests/src/notebookTestMain.ts index df7ace2781f..03c8c7435ce 100644 --- a/extensions/vscode-notebook-tests/src/notebookTestMain.ts +++ b/extensions/vscode-notebook-tests/src/notebookTestMain.ts @@ -59,8 +59,9 @@ export function activate(context: vscode.ExtensionContext): any { } })); - context.subscriptions.push(vscode.notebook.registerNotebookKernel('notebookKernelTest', ['*.vsctestnb'], { + const kernel: vscode.NotebookKernel = { label: 'Notebook Test Kernel', + isPreferred: true, executeAllCells: async (_document: vscode.NotebookDocument) => { const cell = _document.cells[0]; @@ -115,5 +116,11 @@ export function activate(context: vscode.ExtensionContext): any { return; }, cancelCellExecution: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell) => { } + }; + + context.subscriptions.push(vscode.notebook.registerNotebookKernelProvider({ filenamePattern: '*.vsctestnb' }, { + provideKernels: async () => { + return [kernel]; + } })); } From 9b6d13d2987de7b52574b89ba728c38f97a7d512 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 31 Aug 2020 22:18:06 +0200 Subject: [PATCH 734/736] update services --- .../html-language-features/server/package.json | 4 ++-- .../html-language-features/server/yarn.lock | 16 ++++++++-------- .../json-language-features/server/package.json | 2 +- .../json-language-features/server/yarn.lock | 8 ++++---- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index eadd61b33d0..644540a20fd 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -9,8 +9,8 @@ }, "main": "./out/node/htmlServerMain", "dependencies": { - "vscode-css-languageservice": "^4.3.1", - "vscode-html-languageservice": "^3.1.0", + "vscode-css-languageservice": "^4.3.3", + "vscode-html-languageservice": "^3.1.1", "vscode-languageserver": "7.0.0-next.3", "vscode-nls": "^4.1.2", "vscode-uri": "^2.1.2" diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index dc001b1ada8..4a64d9a49e1 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -721,20 +721,20 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -vscode-css-languageservice@^4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.3.1.tgz#a78755b28b8a0cbb1681121f0fa372860f34ef6b" - integrity sha512-Vdz2cyoTP2tLWikhFdouK8dAQ3gVhLPxsFkIscM30Quh6rd/YejTeZEYC/W+b0iKumHYebDeo1GUFbf0ptySRw== +vscode-css-languageservice@^4.3.3: + version "4.3.3" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.3.3.tgz#fcb8c7442ae7bb8fbe6ff1d3a514be8248bfb52f" + integrity sha512-b2b+0oHvPmBHygDtOXX3xBvpQCa6eIQSvXnGDNSDmIC1894ZTJ2yX10vjplOO/PvV7mwhyvGPwHyY4X2HGxtKw== dependencies: vscode-languageserver-textdocument "^1.0.1" vscode-languageserver-types "3.16.0-next.2" vscode-nls "^4.1.2" vscode-uri "^2.1.2" -vscode-html-languageservice@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.1.0.tgz#265b53bda595e6947b16b0fb8c604e1e58685393" - integrity sha512-QAyRHI98bbEIBCqTzZVA0VblGU40na0txggongw5ZgTj9UVsVk5XbLT16O9OTcbqBGSqn0oWmFDNjK/XGIDcqg== +vscode-html-languageservice@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.1.1.tgz#ae9fa52b5415f332d73c65c46d4e2ed87ef81c8c" + integrity sha512-SKrMnoZtI0q+NJEjNJRgzvQeLih0obRvGLh1r5ysd5YGxhNSZbQgqeEgK4jsGjAp3nr4QWIzop3WjP/30BLsPw== dependencies: vscode-languageserver-textdocument "^1.0.1" vscode-languageserver-types "3.16.0-next.2" diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index e3e507c7b6c..ddf75c333e6 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.2.1", "request-light": "^0.3.0", - "vscode-json-languageservice": "^3.8.0", + "vscode-json-languageservice": "^3.8.3", "vscode-languageserver": "7.0.0-next.3", "vscode-uri": "^2.1.2" }, diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index 281f8276eb3..ac7354a958e 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -80,10 +80,10 @@ request-light@^0.3.0: https-proxy-agent "^2.2.4" vscode-nls "^4.1.1" -vscode-json-languageservice@^3.8.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.8.0.tgz#c7e7283f993e3db39fa5501407b023ada6fd3ae3" - integrity sha512-sYz5JElJMIlPoqhrRfG3VKnDjnPinLdblIiEVsJgTz1kj2hWD2q5BSbo+evH/5/jKDXDLfA8kb0lHC4vd5g5zg== +vscode-json-languageservice@^3.8.3: + version "3.8.3" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.8.3.tgz#fae5e7bdda2b6ec4f64588c571df40b6bfcb09b5" + integrity sha512-8yPag/NQHCuTthahyaTtzK0DHT0FKM/xBU0mFBQ8nMo8C1i2P+FCyIVqICoNoHkRI2BTGlXKomPUpsqjSz0TnQ== dependencies: jsonc-parser "^2.2.1" vscode-languageserver-textdocument "^1.0.1" From bdcf7e10cd7cc1ef323f03d2851651d271aa36f6 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 31 Aug 2020 22:25:47 +0200 Subject: [PATCH 735/736] update seti theme --- .../theme-seti/build/update-icon-theme.js | 2 +- extensions/theme-seti/cgmanifest.json | 2 +- extensions/theme-seti/icons/seti.woff | Bin 34972 -> 35436 bytes .../theme-seti/icons/vs-seti-icon-theme.json | 622 ++++++++++-------- 4 files changed, 334 insertions(+), 292 deletions(-) diff --git a/extensions/theme-seti/build/update-icon-theme.js b/extensions/theme-seti/build/update-icon-theme.js index 77165bc5376..85228d704bf 100644 --- a/extensions/theme-seti/build/update-icon-theme.js +++ b/extensions/theme-seti/build/update-icon-theme.js @@ -305,7 +305,7 @@ exports.update = function () { } return download(fileAssociationFile).then(function (content) { - let regex2 = /\.icon-(?:set|partial)\(['"]([\w-\.]+)['"],\s*['"]([\w-]+)['"],\s*(@[\w-]+)\)/g; + let regex2 = /\.icon-(?:set|partial)\(['"]([\w-\.+]+)['"],\s*['"]([\w-]+)['"],\s*(@[\w-]+)\)/g; while ((match = regex2.exec(content)) !== null) { let pattern = match[1]; let def = '_' + match[2]; diff --git a/extensions/theme-seti/cgmanifest.json b/extensions/theme-seti/cgmanifest.json index b3bb41d3eb9..8899dae032b 100644 --- a/extensions/theme-seti/cgmanifest.json +++ b/extensions/theme-seti/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "seti-ui", "repositoryUrl": "https://github.com/jesseweed/seti-ui", - "commitHash": "f3b2775662b0075aab56e5f0c03269f21f3f0f30" + "commitHash": "719e5d384e878b0e190abc80247a8726f083a393" } }, "version": "0.1.0" diff --git a/extensions/theme-seti/icons/seti.woff b/extensions/theme-seti/icons/seti.woff index 5dc3bb8d9b0386a5cd2b60a122305f7040296dac..e1a5a63449703bcde3439cb895f62ea883541b97 100644 GIT binary patch delta 35180 zcmV)RK(oJ`kpk?L0u*;oMn(Vu00000ifjN200000(d3a7KYvDJZDDW#00D#m00TGx z015c`j8%MRY#gMev?d{+VdC#>#JMz-*;c=RSTdKP*<2*s6cHSHN_n@t;MS~ zBpa9yHODUi79< zao0p&`q7^O3}g_48NyJ8F`N;MWE7(r!&t^Ko(W835|f!y{0`HY&J1Rj-?Ny_9Og2Q z`7B@|i&)GOma>fHtYBrae-*1)!&=s{o(*hd6Pt_c7PhjD?Zvu-o$O*ad)Ui9_H%%P z9HN=S9N{R(IL--9e{zb`oZ&3zIL`$xa*4~daD}T}<2pCE$t`Ykhr8V4J`Z@vBOddF zr#$01FL=qT;;(+q8(MkGJKpnwkF@cL&wSx4-}ufCe$rliboKoI)tE~|%YQ$0{~9fZ-vOdl_L9AiR@b~ zvTu#ZzO^Fz)~Wpb*NZeZh%`2eG&YGeHj6a2h%~l}G`5K}wu>}&h%|Ppe9tbC-fofJ z9+BQ&k={O)@82)dJ|NOQDAGP8(ry-M9~NmJ5osS4e`y~RX&)D9pAc!E6ltFlX`dEp zpAl)F6=|OnX`dHqUl3_u6lq@)X%DIAB(h~h_s)Iw4aHzpNq6#h_qjdv|ov|UyHQgh_qWp z+HXbLfA2)v??u`lMA{!!{ts^xIp>qeIiE$&`66=8SCMnRiJbFYh8T>=}Pxn*V-jZNR}*l69^kbykI+ae;_u97;J1vaO}k31jYuQd1fXFU_yoj z2FJ{h@xzHd5VnK~34wu$i3!ZW=Y-&S?)Uzus;^|;2k(7PeY?B5>eQ*T{FmQLkHB8jgmYVW*v-7Y^Nd=+K?ND?b|BUcGhetG8V}*tTt;F{X2cW4!x@9_LhH z#aI4E(=|QaGWERa47Y6)3KIxff}<1N%U*j~uLmDpG~Zm^y_(b-cQ&hE$Cyyc2)XsN zrV;*Tw2FvO9e=CuYJ#2}B7~VRTuz{Se>m+UB1^fGxtpPXJdiE5kl?eZxrp0IQ*ayc zPSWhOH(!H(dk5-|`lB^88m^*l4TZh1*M+`yvoWv`+)_b)7|37AQbdAm1h_~B{n7CJ zD}*QK6WeDtGc?aI%eJsRH8$bHbX3pwc<6X4Cxk28remk3WgQj-sL~f5np8 zR*6cKidCTKzR)izhT_-+3#$5otym^O*K)qJ;9JBboU5Et&bdIU>Uc(Jsy!Xs%wuIk zGrac2nh@|Tu4Lxk zxturZMM)qbF3AHF2ADhhlU(4;vU^rBHONCqP51fMxJ zdu%ScEUfRW-@a7xZ5VMi@M?h_$6MOXEpgt^72V5ss+JY^TJPW1?RRIoe@PGB^_}nR z*m3z)f7EsqrYQ#36vgozziQ`|DTAv%S2V?|hfY!C`L{p#!Eb-$;rD-R(uaBckGXPg zEq6sqKTT-vA_i(1^d)HwMnfQh!1YBUTAMTgq?FMPutIw@?4~pmK<_&K=G8thiM%?H z#bgbk@PR59(wksB)=jb8h7yr)Vv@DA!v@$f?uVUsWOH%w5WO~ z4MPNK+b&Ryjt{79Xp~ZvsMZdcb09mfOitFRn`gq|)UX(}d}`^Kp&X}2XE3rqVVLxs zKpIiUdtF9;mp9E18t^_bBK_Y@E7_Q?m=)o^Z8Ga;h0w#pZy`U%WgR;HR5Exa;q3&=5G&2}x z^RS5ez_gOtlSu)Xe-l2+<}2meb<~#?4SVuRZ_tBJUo;0vz!Q!?v#mg#cJZ`nF7yUWY3 zA)YS|Y+ov@?o5N^tYXlZ+t0mIW>y{A5n7?PqBt4?5HwHia`v8$T~ zW-!C8se^lJuByCesb)?^M4Y)wD`BeX>im)>0n}b4qh7`v>!>Y9e}N3r0;yQe6ihgj z>@yLsRVW;VBYCPOwaZ}ChYAKm$%v9mMqQB3FmbX7kP}>5Kpl|o3k0Ys4Av0TP(w?S z-+Jl%1I-8?z|DhHMyHyqKNM2n_vGUuxF1^6fd_-kfA`@ApqX~38~VK2?n7I8Q8$FP zc9Jj^Q0>HmE&{bktpYviw+CU`;H*jMs|THmdLd9~r@MyQP40&s=wc-6?6iqPK(22& zK1+SzZU+-gaae8}z__u9wwEdvaX9gp^KlrRS*unE-KLcdw+#0fD8&DHh#y85zq{%Nk!5oXbv`OHj4#L1jV3}vEnGy z5V{#yn&pt=1|{=TpoNG{%Ar#RQd0~dB&`gmPKV9ns5K4hk^E(toFY>r zSOpSi)l#g@7CE;)pbt4dgPINuXI;{|V(H9Dt7K@z#47X(Gs|I`Mj2C81SK#63ysE7{j(hSRVbJm*d(fk70=L;Ma;I;S3rIM zUt$8S1OXnZv1X`TrO-xkomY>Mej;N&y=PKA=i$qV<>VKYe%3&6us zlybr*fNCWo86|^W`tFJJ4FX)g6Mxi!^WSW(%_pP9gYj;~(KO!*^I>uA74(DJy5Mw1~5MY|(Lpzhlf^(30mnIHsJlgbdl7)^;KK-hGpK!jmV zp|vd9!4GOtaOe2u?;+>C04u-f_+hQuZt4FrXx{QgOnwV!)8t`X8VbtFe?XRPN3tXk zJ7YkL)GTb_3bs}m&g+`5I=aim#pr8|%MLrw%*9_z(0gNbJuZ|B4X>cjJj6PuVrpy% zjpfbd_Pl0@6@qaLxWkM=#UN00Rlh~~`cG<>Cv_31{(||_+@`)?!v8I|CATAYnWWle z6J#$S39(ZxAc3FkBLM0Ne-?)VBuRA!0Pz%ZOG(~Jp*x3>0nuHRVm<1~8qf=^esN1P zo&_U9v;FCd_9#bYZhEy@@Z(SmG%UzNhXr3dy>9yTa=xCQIr%!l&{mYMIar8qLzDFx zy>eUCFg(@5$r+HSI`@bHWO*#__xXM53W;rt$?VFtxLN%EZS^=wS|Rous8cm9mE*3 zsxj!IXE80R)Y0;)ArxUMP{J~)X(LdHt^tZRE-M7hQ-i3n3%HJ?$_wl};X4*mje<+y z6vidPAogq3(22FYW-FRssX8h~j3YdicjiEUdF_JNe^!fSqt><;`+o7LA%qb~bO5&- zIu{q_(jr!FklT~HM%see^uWsp_Jt9ZgDMfV#9t_#0eB7D5|&MnPCiq)u$ zx&!9?e-+w!!<{#-A-pl%x^wNiYW=4GTeijo z$6b_rHH?@Xo(huIZ#p!fufr%Cm~5~-Folxle*#L~2&pwJi8^z_jCj%}O)gS_DM=a* zq?s(zi*Q3asObAzfs&}?)Ko)PPga`E3e~uq*Pvrg-g>rWwq}-(F1dDD3uhaxJURH* zOLnhqpWZU1KCt`tb}}1;u7NpfCCkSa2k~mX0tAfECF>jLd)`u2R>DwWHw^J7$m=RY^gofD3L;WtC_##t%n-5k;>6i7mY#&>Ia$t&)&1G9BmP% z#x8kltGy{7zk>f1^l&Yc5FBK`kkEK>LN_3iB=Qp-(ZOFqMxVa!bme~i)Fu3_}sXlpq4(PNK{j$P2z3E7=<;hD?1 zb?Ko%fS&;0M58KN!vLpTn20qjhuFg8=UxNSmu3s|B7)a;-Zb#${-*k0XRkYZII-7X zH5^^FB|Ln?Yj>1-7f%hZns=u@S^T4^Qzv#-%>I%2g+obx=M~3`RlOaoj4%Dkf1ZCK zyZ7IISNFxzPn&-2(An!R?~c!|pS|%&ID7S|wX0u7x2#@!!^{_=|5A41^%IxxpBX>i zJ8{KEeyTlxLnny-5f#?P-@72^ZvZ}hrR-Cuof^KGb=AuRAebhSXutx>j4$Z`Yo}MT z;qDCjN#@Mp+07D`EJy!=%~3O16WE;DCS&?8+PU`Wam@}VH^nPRh(xe-lo1L2OE4Fjd2>Iz}E0H>OX6_`)_6Sl0xz31}MXFku^T znHd@=ke^oxU=^rDI3O{8UL_oiID=-Dzz?GT^Zd!cYtE&g=$T(|hY6M|mv?BKsrvup zxtxKq%HU3Tu2C)jzdSar1WIZOw|=IQrbrtvsVi%|S=_qWDEPd9e?7-wzpUhT!7Ts( zvR{HE=t&>YAQ=rV`~WDuV?J3)%@$YHeKYg|Fk75yFk8&smFd>v9Ow$m6lREqbKNfd zeP*z_I@pfs>ggT6)0SomAzamyA($zCUe)tQ8h+lk8g{{|%{?%fTUnVSYiomzjlm|J z?jSdV)dxTlsLo|Af44&xQA3~?{`bewezgCw7caXEow|(N{K{93?|$-0^yXKNU-6#z zyl2w?>|QhHz3lGJ2&9+?qcG~6ujE^Q_=huZd)tve{KM(DzV+o*?Rn3;XRf~i<=*`s z_{!RY{~rI>Tq%{)zsSTJC1M>V0&T>HPuoY$oZ@IHKne|Pli^yr*@(m~F-jl(HGRswI$byEi-ZW!7rUJBLZiyUS!Qm>x zp`SD^*>HWvk|V2zwzlIUTC3f2wA($p-({*M4FBNXe^S9Vqsl(vl%|Tdxaky8@>LgL z;Q4v|VNT2Cq3=GRHy1Dlm<9dxXo$c0$nSpO6Yo0MZk>B{eR$ak^x|9IhF;jHUb+24 z<4+yEfDOx}1U1N!%%zevX#yt)ehmTlNIOpkkJ?ECNf|nD5h=a-8l|KctdAO3DZw{> zdUpI(f8AF?u?MT_`1|PObGPXn-GDo9x<)mWll(urMm6}xS@c(6SnWaM`fa)!pw{@^ z7oJZ+=d}Q7U7qQ^>;=kd*vn*U3Y^XxngB2&kkv`5+>-O9rTqYt9i{Py^jfM6vI`(A z0Q)U&>l&dUOh&b)*T1NJ$uC_B5jyDG-Y&0vmd#Anhj zB?i-P3=~F$^4=Tarwq`PYeZ*Q)2@T_OgWD`+xRQz`6!1PL1Q3;DDdsV8~r+Zczo?Y zfBb{|MBVWh&=-cE9({T={`lwu&c}b1;{2wF_~ac=li3^achLBnq9` zmHT#X>vyL^by~B5f*dj~X|@^K_WgQLHlmur43XFSufyM1)7H$jcTV2&-+ffR>=>0Yde>QiRVnWjzySO%X=m=Zj8SS65o=s$UsCvhlv3Yf94>1{^^Gv zdK&vrzxTaQ@4GZZc|Se+4d)@YbZl8TQG zNaYHQK@C*9Zc^T^F10W1^piQq^77N|t6Bih_RTmE5g&I3`0-N3txx6a)z+57{n;>2 z40~#R|3$|S9Nu@moy;wbe+w!yYM#@`{Y-l{atAN8=jXR30;D6|B9n+J0N9SmJS2m@ z#6f)-)V|Q%~E2m8+%l#YY-b)%mHFdFQvzcjs!w{zaW> z%+5E^u3>G;H$-U1<-j{&3V!iQW6m*yEhXC?|HfulGpEB8nSvg(f1EPmv@n-bQKJ(L zTF6CiL)wHWYINLD1HCZ*HeS2w&hcmA`&Hu~zvce>?;ro=-KhTdyT&h~?~K3wz_0yZ zbyR!lb7&{BK6w8F_oJn^zkU4eyDsRDdHj7qOKvX6B3K!Q(Hww;Qu zjLxku_Tw+zv$po)f8tfYG4I=VUE zA0id#{XEnpg~K2)tbGKt=!H-g~i!w0rKp=N@F7dGcyFy!W1a zFUW&=vO}Uje=wN@V5(7PgkCuF^+(#D`&|3szb4zCsy+1eYp?zKL$yuahF>73fRCk* zy)1ny9T{W~m?!0;Csn%~2#cHcthw zumb>*OG(^L!;#tGXYTzdgmz3a0zl0TGGC4uWr(8FOF>2IaZNovQV-N*0>GM#7)Yx# z3y_W^0JTwX1m^oFb(eO+8t!#omSr;Rv?Z;sqXFY7cmZ7;g~LfuHH9xj8AFxTbCFd( zo621%e?EyR$fga+trTlmTf57zu~wcp!Q3_i6h~`9wJ2CGpgxIkD5z8!M5I(0 zxacUR?kX-JRm;?IO~$`fO?S%7ER(#cDUFuJ2x75arNKf-l?6bC22vCn-@{~~0gq=gf9zPRD2f+y>spM?-W$%A`*+?xt9%+! zrUgu;3gen?t187RvkF?(0?Jm667%uOQCDqO?2=`LdTbNW z_s}6tC|tTti7M?&#lY2N8z84ZfzxhLyBB#Lx40UK=D`YOWLKRTtH^ ze_{cEMo}%ucV4U(5j9vwUrA(Py7Q_Kg zk^IN4IZbFxoN!7`;1&uO_~pyw?lc#t0Wv%RK<5tIbnfn5QvxOX2<^~sOj(Y?lWAbl%6e{ zDi9HI-sNc?kI#L7J6}N`DNCO>1Ei*^TR~|-qg#qB!DdvU_U@=q`K<%W9LF2b0hHEb8 z1468#skJ4~P@6f0)6%viS#CsOX2P!U5txg78+#0H|Tcew}lrSjs!4 zq6WY2>&no*7qEKX2{0KD4t0_=o~QUw=?}~0e^iM;P}D2hf4~%DG7f#RC3jsK`t<{N77Ea+?l}7AcI{|>dlww5zA=|vXjW5WGd3SBft(5WU+=pf0)RkSqNnZ zf(PI^Q4s4v$s(NTRFj60E@F@H1N)(io@(0$1EUV?E(S8gSeS|(lapdtaGbKu5aF?^ z)eB|ERNJk6%_&ND-Lm@?mmGL~===V42Mm*dq_`IDbQXtehM`yhPnwugY#Jn=2MY?! zAWV#U1G-VF@{l(=(ti{af21*9C{qwwcEyJw+%5#RqVBHUC-&Zc%(CP7*29-va`4ue zY1TpC1~na|`sII;6M!}1Tqn1jJDNL_yEXT=+)t5$LNq{o(dFpX=$Fthqc@{>q6g6X zq^+5biyS~9jmC~2c87hL7t%?sXg=}fMg27M1E5#bh0}q&24$M5f1pm9qy9ST4y4>4 zM%~n#pJXvIE>n>J{SVqQM+uDBu2ioum@-bVM#6w3oiu4|J)P6O^ko9!PO|ET1GsUN z2ER)5f+luAl37@!&J^jCmL1JZ zo>9N)_u!Vy{ON|G3)iG+8AeUnB^WE2hcoh<{az3P2lwF&^aF1%CXD*lY~Ter3bdWN zt?NS3UtiAKLNTWSXsD$MF)RcAt~E>`OrB>L=v2X$W%7Ule=x!{O?EO(4J)AZn4l^q zO{RkQ2ArUAjLKkLF~m%UKFld64C}6FY9JasL#V1H9kU#)cufZyUMx^nC>}n(R8uss zoi_twfaPmDV74+zUyNELc8e$vf>z zNfqtFl&c6kf4|-2AhV$g6Kj@X*&vZkU6I*67BvyDrEIdoWKIA#fakEvp#4QTEVkYR zjrYE6EL14RBnrAwBPwQ&!41&Fi%{T0UDtplL-?V-Q`RHHZ={5pj8^k zeu|Aeky$kek4uCxW?DL}EL(vQ=l6LAg)$nblJ(kf)}TbhMjRN7`AW&+MTKc*s}5JU zjP|uD!48#=WSkH+ALvb|6b%w8C9?`$GPxAJF)I<3GJ>&1M9K5(mTvpd)0^3RIS4VU z<~yc!e;PmxXm&s?iYC)3;Afz6&Os3!cW~BN)l!&%fi_I|A#sE)Qqsas5f9dh|Vd-A`Mqee3T!@_k+%la!Kye+-q}h04>)9L9vRu zzzT`@ImdzUlU5o_%aC9o8E_qCfgUIjoOf@ff97H;6QYTT&$3c8zzX6->c~_QfzY@D zges!%CrH8gyXo;?5CpHYY4+hMd3>W&Y*I>fZF+hEHHz~!<1#^sp;7TDLP}-%@UGsn zqAV<3a&b3t>2K_un^yG2!uFX1*DLzk>_v)wHR6N%y2Ycy)YJf1v4OEcrsbDTW{8f7 ze@3nV%?Wf0+O$|56{iVL9EF#CVK~Zid#46^WNpv(b<1c*)9hbSJL&CPt0%(Tw{~iM z4jB875D}!&RIK&lqV9O53wau^Clsui8$haemOP-qx*TTCML-OhSX@PEenVHr$S)jcf9D710X)9FWw}7Ue;RJzUXYn~D#=IXnNH-F z-1hcl`}Tc@ZCzbfY8C>GT=aodDQ$MbXxUuYTWbc0Q$YWS%&pE*LHm>=I=5{O;5F?r zfgHbcvQacNnH_4Wmi5)hu|gO`8)nO#-r9y~6kgQ`2x*Cib(y6oh7EcZxLqf;e}ZY4 z;nD78SKSN@L@S+6h4RgBL3b=~t8DBZ4W>8k!RrBMPUjBiZjmT6jh0RNn0fz3jhAVa zG-?KlYBR(bWM}2@Zr;-Dbcbo8$|`F0x>@!;40@_4q%O*+IjtM?CHk&Pw3>j13gBwS zu83N$kI@&x_A8bt(UrVlw#`Bye;f2>3c@As)-fll;BK>Qs{j@VHXV?Z1n-^(DRuOv z{9#Z4og*NVJX@L*bao2}5rYY4IHM9m6xE3ecvSUjLABIN3e8%3uAA&i3cX4*nlg3Z zdteOYQk~(ErF7i95n4I{iEnwf=M`L->Ed1rf{^V@WC~#%#5Ge9o+lJAe_YxYb9N$S zVuQ|+L%9v$|EqFmvuyNETjFw=6_aJLc82ZLiI~PDEtEm`W(s{}_yQubV z(9P%(OdsXu^1NwM^(fa!vDGRX;wWRpG-sD>xGIe7>M=IC%8v1FVSg zlPahcifSdVz#c{0Wv=V^$wwJ*)A&};J4VO_e$SEl+=p_H!Dwa7$PrJVn=>d3>=T@ zK-0Po@IfFJ85Wfxe|G|O7%^$#@>rAy<(S7DlsGY{V_^)i#$-Acn&HGTXfemdWoFq^ zgqK%Dp;V~(nyq6M^{Z;l)Ip=de87!>Ena*V`W_-+s1^&H+NCIJ!hYSzpc8~{HWxBCm9^`}nZ6cHKza?)4G~Z9-LLfWR zwH89g^5x96gWr40!p!Me`^F&^9>(`F$?R>l|4Ij>3#>irwQ`Me{6`8m@}|yDA$ceLsNumSjs@7 zmiaJrI6i*Kp(CiS#p)RsOk6@tRpaqutosISppOQp!tYiX*iw0b8Kzgb4aTss-g9d0 z?Z7fbaXih|%z>dZsu~WT-LMom?Fk{2^ZqRSJ&+}4f3BQs=4N1Iav)J{%#*=()LR2k zBg0||5W39OPdY*#N#kG^4M~4FfKsaJie8&_KPa`se@Kdk8 zp8V&Xmy<(Jaq=|APZQL9=Sxpx^bEWk?`SSQ{S-zjyk9&12tx3^am5AS_Om3PyE6A{ z+1deVf5^-5DoAW23s}Dap{7f6U*{@xj$N#UJbvNrd*|~ z2jqub&X%qTnRv|81E2vgJNP7k1*QbYw*Y5Q*5pE`MMTY_t{9}orSk^exl`tv!(0*E zC>TKHL}p1*g_08+wQlUSN{uiNri0;vLLn+>e~6}Elz@=PJms1W+zViHca4gxX#sFx zV0sw9r^g+`wxB0+wFl!V1#o;wwV?V zUo+L~?d$cv{1=mh3qGoO{N-FVH^^-REtf-b-9{KCDRAg7`RS^lM6Q^UD?Frbk`i*z z?)0LpWB{}=YJu491wf57=vj=wHNX!K@ zNSyNOc7WkeExvGW@gw-lr5j7Hxa(pPo(iQO4!#JPdSEJjk9QHbVm(aWcrD5A&DVM* zGJDw-_1xmCN^3`Nl=4M1if`6W8#Zw#5$3Pe?%+l z5N&OZyqBCi2OsB;u73Ra@!{>?fBf<9qd!`G9=-8ruck-$J-+(<^Q%ukzWw{(-@fUS z#~%lshG8CSxm;@yNpco$;sVuY%NJ^-ir13alb)(k<~r(t zZ*2|N7W!{wZcqh-UmLb`bF9=9UK6dk8YiCi>YG$*aE}~Zz2iy?f7k?7tF4m&MYFHd zY+fliF|j>OcWtWOS)1P5e>i;bJ$5lrRZw*8k$py;QI)dNO6GW89I>dOdX5=fuWrU7 zEc`>5>BZbdxdXW?B&WI!nZPGI)NjwCrj4bjhOTBvLPiH7nL?lP2_Iz_WnM&jHtb}c z6`AnRZ%PQmMQW;a+QfY63%L7(8b~HkLyT^K=JzOWGR$>=wq-EYe+gX#l>?2%=p;2l zfXKKEN;%MMGjOaw0c2}vh|DsC8UEo*KX?!^eNmUbyWJp=h~J*`qcahqGL04v?qJ9U z{LP+WaFJ}BHa*)^4buaV!|_TCVsLpv2NnEdu+xm32Xvl8nJn_dmP`|BxE;|EFe*K_ zf!`N38jFp__)pO{f5$866F2Yv^;=PQylXqU6MeA`7Z&S(Fg}Ow3dSG1^XP*QppT3n zN99dQ%4-;V9O_wHEVzxVcJ z3h&>(29`={wVa7-s^*oSxHhWR=c;w=?BBb*(^A!y{jaTg-ILpo)_tZO-+3iWOH-)h zziB0RJa9+S!h5&xkl$wjW}#;=L9wAd;KlJv_1QZ7y(|t2@*~bI<#tF8OTEUEROGbJ z!%T;wG$T|leZka_}w(Q;a`|@b~`LtiZ zy7%ZhV84aL5b>Vd_a&=SsJU&g{9MIv$?sY+hqIe^r1zdUF#b4dHE;jTU)_u5Jhiz* zSGMlEeK)#ia@V%KJCcb^S|D4&{(P&PGid2>vdCDzf0Aa9Il6<)0Y13^r?c3dRQ_Bf zDF&4Ia5I3HVtg5EO%pU^hz$_pa3Gf-rmIQU(q-ZjtV^SG5^#G{cw42KDS>PcwRp#G7InrwP=NZ3ZU7Ed z8;$0ADO3djYZ0G~je@W&Ko%vLXM~AvPt}&aSwQXZQ0f@w$%7tKG(#E@T7{bV>J+vW zinX|6Y-r2|14Iz&L%+`3RSnX}`6-S_DaX&&e{l@cy#zXKFSKxnw>u+AIKv&Nhh8|k zXV>)1%=E53k8U0miQSwlR0dv4uX}NMoz#Eka(XcSNMRvyF^<9o%V{+J>^z42XZ**2 zWmeK$96w#B2}W<4^gLN1(off&OLw}+NFZ%vnoytRf}(3rOf@#_DP4t!TkZ$*UF#2C=0K$$`}pHOydu%`Xy^9sG~6x?>@-nZx=h*`r)RPFLB_N2=gS&KGLv8gdQr9obYq7v zEla^SO#2%(#2Xl!kk&C6(4ln9z z8pS{y!c{>=V1*+wtJq=43l=w2e^Y8fr^0YvC{&Iowo8mtL-+BW*p3Aep=TR%5h@2u z1p}`dgx$jnfpDC*;`xR^%&`$E&RnF0#kn~nQZcZpTm_C;tTF+pkd&ZORV{*;sxT5PB{Bwpz#vo1WhjMB7-WVK*N{Ml4K`Wp^$451pj6>G z2#2EVS1ULEUZie`hpWg5rB9^qWBI z!33p-RhLx!qxl_ zrT>}alc`n+jgm8Xe-GDT*lW<1ZO}Ygvf%6|(TJ;X9YEyT6#x$KFn9^QQ%g4JGS@4Bsc63{+ryFa(|WkPVS#_|1@z
    Zwk7HiB;5zY$#=# zDSN_=G^`0d)%Y=DX9Qqx3?$T%n^L4J z3bU+ySDn*7I{m_hGCkEIV5OQ5?gdU-r zSBW1Oad!pi07NiYBDNl=iRZauX<;>vxnV2Z%xi5pp*Vg^&zpv=vp8No-E1X`d{IqQ zuX%b9cTF&#TfU-U~-H>i>{j}?I5NW~2myXZS8kDQmj ziSpwg$~SI)C;J+I9ZiqFH6G*U#Gmsdf4M4mZ|?KCufcP{22IjrlTa?K$YNyY_f7#B zH|T@SIY`5|Ru&cOZ01$VVgQuoireh3FDYPx=>Yen@K}1pv&#|`t-Dp>QcxIdBp^9SR9R+K8tzLsXGt%&JE@=xr1MUC>2TOvlfeuS&zV>rwedc>e}A}0 zT>tWFOv|Qb?3}Oog?tz&YDJ56Hza@$gvDsdTPy%edz#7eYoOVgV2;X|FQW%GBPx~3 zQfHb0f(g_Th&+v3J{Vgt@~Wm0HPmD}2gepyL4r`M#`WS2CO{kM$Bl@^R6?;1z(GSY zd<6QGF-oVag>5ply}FyhSI7^7f8Hxs{}W0xMK`2Jo+S8GZzpM$617@cP-%I3ex?fw zJ|FB#+GS{use$%{RthU}O=d1LkNK7#9N!8Lm@Svi_G4e+u_&G`IXfMIfvsU<$DW47 zRcrZ)&;`7&Qe$f6y;Y<3!aM z+^Zcq(6eN?zPs;emnh8MZa)qt=hBzC&%owk=(~}Pv(A< zt_cd{j%QqYVN#(nCWRcf^^oOe-FEbUOSO1PP09qWLTm*81>UfMVt8vBA6_rl=;yze{CWJ z!P-xA)oj$9Z0iQ}3eX+w&aNNSP!)H>P4`&l9l%4GR(D!dgc51~G2g5LnEVTat%|D zU{PsJ7t5d^e?tOHRgLo{z*a!L@KZOOd6^Pq0|5{<@2Vsgd1ith1YrxtzPB&GSd;tP z$Oo#>2qCC2%UtHv$X&b$19<<4k|@{o0)jl*G6pCgpewImtPoIS3Q-zr%{#nkK*el+ zxoW~$O*Las*324Mf9^gvJRTCm5V~uCD$hgj<07w9Qd(RHphCH@gBWI&QVklh0tj7U zA}&w+Ep)lm8){VDDix<3HiK02g@IiJ9iu2=O$JYhOeZFRC3nM7fgSnyRU6XYAyTQ6 zP=$xrLM?o(7BAKjF=XHcI8?-Z&DpR^*U6-f)93wF&mo{ve~?&v&?~@tz81X+y&e4y z`Y`%^^bz#$(Pz+~qCZ2=qQ63ai~bvW4*e7QF<{G=Qu_t025eyu5N(7DIL1|6!&A70 z+jtf);Q`);*YPgAA0Nh-;#2r?de+WN}AHk2}e~UkgKZYO2pTvKR{{%mQ{}exoKa2k}ehNQ>pT++Le?e-K zZn_w|Hymt3132#PK>bxDU534FY3vKE)1KTQZ3I__HE9Ri2uL?c-HOSuE91{{12jLo z%jdJW4JUeO(5~qRa25Wxpvgg#hwCVFN%Ev2xdVPTf8_5*(?)*OYiwSV`_d%RTiibH zHC}}W0@@gM+;kJXFmSUC*9Pt|g4574S4x0^E17B-^+0mS_ILYaC@YqmzO~_@0Dd>0 z(gJwwrmkxq4BA28SR zxo$s_e*-G-ff@n&L>0Noj~H}h2iqf%4AHQbIitF1g&;v#4~oc*9-+t0{s0C2cGT-M zp;X-3wW|zk1$$>_h1Pw1?y-@z%TfcSI>VzDX0N%>r75`c>0a~v zN)aoAgt|iBWJ<&KM?V6ym5zVF=~A zyi<(slcN$s>yX<;TV;YJR47%Al5R&LrRWKQ=H+%o<7Z9)CqUT09zP0q9Xl)yrbF`f z{eKi@hU|r(7p=+Pzj>$JIwo7sfzU6>51ETkNRXGhCY`LwMU<8s!od=}hqRmBCgu3~ z3J^Vv$KOw_`7{BNe^$lfVJHIE-*}QV`65nBX~KpZX-yV1@;VH4$fV$Jm+6FpCO$IR z=TJadH3eO)Mw)P*t;(ctyqA!DFd}S?rGE`UPr-zU@lh;HopSUVnOrHv_)D=Nr^kcI zLWds$^+x`NjV-C0S6Q03NjDg}PEPBGB`wIEbm&uX2NYF5%L)ISoaTr4`=q`Vp&Skx zOOts39UMO@dO#t07$B#lAl!nHc{fz{cQ^eC#m}W)jxsM3nSYk^ z6sGH`hvo%%;`?@EdK4qD;?c}_?(jaDOF6C)qPXjX%n)LD_7C9hKqi$s@}~4o=^`X@ zXADqV838PDITybQN0-6a;qTVRUq2})=EU)%DG{K9`_jouKLIbnu&q5UkN-n9#p&k< z2>=e$1BlPxtriM|{3oYT?58?@+SO&^kLRTs=0}kI;T5Ds_Tq%llpyud;Im8EbDf zvlv_&%<(PcWf}MVc2A~~XX%^hnf>SXA6VOY$-X<1sr~0VjirTMON-|ki+^|_X)G*1 zvH#q=-;%U3+P`D{!E^i3mzFlt0!_HKb76C>iHz~fT^WSibEDn}K05-fUwhZhhi<;> z+D{TQ9vf(Q#~tIpKrh5kO>D<8DW-MMS~9Z!ULYw11#!nv^qxQDwerJXDGTtSY)eamyCQNx@>wr^t_991vk9;>}dk> zSzQkTT*xb?x_nH_^J`D2>V>s&^Z40Zm@DS0x!E-0vbm97Pi_H_?te+RiU#Sz=XCLN zL;A=jIKY#tTjtLJ6bNvy&}bB#s#w%UNqMOZf4}|PA6ZbQs<)rN=>mC)0B(_zBsf?KIqNO@& zUifaf=IJJ7Oq4FnlK?)M%5`$9xqZ1`l2)CJZB%4DX^=v^#Lf2NOY`E~zO>;QQ77;w zdwI#dkg{z~lgZxT>DCq^WvW@ZOF&wQoc&c9>yhvao-n`_@*q(h_f{`_{sWwZCi$AD zh?ok5inrc63x7s+Z#QGDeWx$q#@Oy&ta`2Wc6 z`V?xf?_ZmGqwcD6LN&yUZtF7wj2wq@`@){#41YY=N`E8Za;=NVa>TNbQno8kloC#M zr7hTGtJ?LHIXhyq(?NoMJex08T+=p6Whc=b%?ZNQ;X6mGQJmKu?0H71IK5sO|8=)M z8oX(^H5pab@-sKw7{z)i@O^|o-b9pgRafV1+m4!cY$bUYyI#$#!p?mY~ z@b9s?qZepx^O<3;nfmH}{xjJxe&m-=9RDyu&42&zCuXwoZ}FGYeLQ8PUOL1&B=S1L zZ2N8DfsAdNe&ZF*z?!@AmMdq}AYLeV>f)6*ZYMsT3&!Wdx2Od(hlf4a*b}ok%|)U?(d7I|BoZ1A`8r z(Zb;MfyQ>>8rl`P^jxB$MbV7ODW>bwD34=6bo2%n@r! z<2aP|H`wWIl;wQ+T<+i$T=<9*x$D)1H~#GSy2ku`13lQ7$8+O%Hl2pw|Co>RxFR+NDV<2^)$~0eluto;`1>70*QYT$&vj=4Jpap_NCVI&DSidO- zz<%984AS5T@WOM4+=`~@hN_kGhumuH*rri)@$c!HR&ftG_1Jdes(UD3)>PxA-!8Y> z+gj~v_J#i4^|-Zfu;VS)ynk&qI@WyTx{F7ps5bMPQ-|iJuN`iCRcH2+mR{U>^6a)9 z*UW9%9i!vJYi8ySO=^6BGSmmTa~j6xw%pxv?^G{K=99kF!6fOdGk;mk)y*;v(zQoH zrispPc9dZi3(3T!G+fY5(g95uETl`Eo9XEmoEv3X69CRZMo68{mXcnU+L7&{g+>_Vf;eQV zQLV!lkbFCDcBP3H)_)2W*Nru9xRQ-oNDpfHHDi z*ib>P5NQqp=c2M{2zZL&QGBqi&+25h(wy-FqKdrgm~kcFOvwM;*Smm8a#d%7dGC$5 zH{KDMkr9zul~q}pS(#byM?Sk%-BnVzT573ZNNSKkAX$78*ng5?By8{i#+Cq^K{ggM z0x@8*UK@tT8oYqb46{gD^JZG4v zzz8*8<|i_if`8O7Z=PLWtHuaxw@fdx4R5AHlu6(E85+AnyOoDxZ?oYnhya@>U7i0y zhd1TEJjA?bi19Otv4|bumUgzmRv!sUY^==O()4ke&=bMI2OUipnkWY2%lM5rMuxx# zwyEMpco4c-$qwxPLKbyo>@*$HT#dQ8a_Dl;VLU1?mw%TU-OW^nsfud#*i%911$u#l zr<}*bE-^^M;n+xtxJfM9-FAg9Gc!IuPi(H_lWymEBvKw+aw_fkaUl#&J)Sk*>6mu*yvb|`Cu%1UK1w4*GX8y#CH9qD_oD$hsFs<(3fY}<0wL+@Zm*Iwo5sVPR}9uN*zS8m3jntB@Xs;}!#wTwZf7sVi)n zH%UgBsX{s!g9q=YvAc(JKXjYfRHs?vPZ!=*_-Nrba|BGi(UQ&JyQXYG@5F%F%%FUy z27gbM&Z7Tu<1o*7fMA$3H#-Md44!HcNf}pd@upTz5Pc)Hk0lul(4_YBWD6bbBB&!P zTR1%GQmHcq1$U+hdmroQ17hYQ_6~C{jiv1nOK>F7Af{vnz^)>7gT>+IBA8#l!Z5qV zidQ|@8v1l-vmQiBha}TTkR(68+=7N+$bZrL{_5!};H%(7Wr(N-aZNH^xx|<_m7yG~ z*xVqKip0KJTr#*Bgg-Aiu^>}m|4|sGZIpb=vAHcBcK?#?OJO*+l8zk-8VGAS<4l=A zjT4SbL+({KuReYCf+tnca@UsUmj<>8>@CD3^Vkj@(y+F6uCY85s0prVE^NNo34cvy zN`pCM6Y$YIsZd_gI`4X>imgCamqJe_#JjAxQ-^_Q;%M|L8qAUaG&`|GYPltT!nACv zian);2gLa)mRTWgBn`xO0Bi9reDTs>{}rM**vY^AOL6XxPSC$uu8&BJ3w6>ijufsc z++O%6g@3C3ZE1!KbO>UOo!D)uVt>{fk7iLfqH~#kFtp>@>WrGn@NH!9$i1Wcn1#ud z5B7_o$ygpzp(eQQKf0g4E)J7yDRb;{WiBoSmX&PoYz`OOyWQ8PY<_VxmsKsNTxvD0 zydq4Jg(M;2giz7%$Ci^d(zF<#?Dp7leYml+Sq@62`Ov+x(JGZ4tD4OX>wk0nHo9#w zN#+cM?VJ2VSF?06%R;AQo}@X)j7!gnx!T&!;P}xC{ng1Iw(ecub&V)mYOF0c>$U&2 zyilh9OK^Bm=GRV{MJGzrdUh&Z>7T#i_+V$PR^>jH2qzy7YW3#wT4O1S40m_^-dF#? ztuq9K=y{hi2ll#(%K!wP*i^-9PzRerocY>>T^)H$M0;zI*X6i#HW!WA{J8 z*wmPHvsX67s%uYwgBn=$?6b^&^2y0(zx1UyJo@PG{T{n`=be*R(!t)r0jut@)nn{n z@5y8PdwXR4?|p8sa9EzHaT(VOrwiu`cj(yF*B`E8$!6}jc{wULn}0!K!+*d8B8m2b zY9;!O_zm7V$j_3E)6rOV!KKx>2{>)-kq!?B;K`41qGKM0EzQ30&(oAEFD$LTXk}@k zT=ZSv4VA-bs5&DzRZSi>1lW#pRVbm;KYZ)pli}JwMlui_3BCZ2OA6_Su^A zXCCR2*`3X?>zz2)>wld);rZ8I*tUom1%5tY$Jn@3je|3UpyYc$8Uf)<;O!JQ^PJiLA^Uo8d)C*S>ZZ5pI z@T-Ln7Cv71WZ}09pD%oY#^5MZ#G3WCeAdi8;Add_tx;#3hdf}5Y?_544|dpRo%Wy) zEkwI=(4o?^K*+ac6mLdARa1|^onmG?$;R{&pIIo z6!5UCk$8*r`s?qpyV&nO&c(~w_dfTESA6c`jr1pe>9cn|$BdVM?zvaI`4u0!Y2w~E z)6JN$3Y%o9-bJ#c!18_9bws%9DC~akxQ@Ni8-LJ;XcegjteN5MkTA&f{_Q~9L-4{R z9cF#|`e=&o3oIC%=^6{Xe^u8{Nhcr&F))ukKhPg5&K<%l3uiOW(C!|K7&3_#G#V zN`C)>UQC1;VjMWJE`a1Sa>^Wb-z;h z-wL0}qi25r#C?G^a`Pk4QV8_7;TRAMGy}(&j@|q_^~^9qhR3Yj-|eCQh8n$9D(yX{ zmxc!2VW-t?r8o{iptO$F;Zb*}Jsh>QamN3pS2;hs>O@=hD zsWd97KN@uV{2yxl;qZ8OXWkCn`C`eDO!%Z-De2F@uycG)82r+=;kZ$^9BbhPbr1Pg z>X!UYRK>R_Fb(sQGL^9%1k#LOlLgpGYziP=7{Z86?x<}#kL+2Ya4rgg@IKF=QGc<` zk|~dpo`m%lpC31rRwU~&+^Tf6FPT8^na?V=v)J79=gOn4;lYtw)G97y)n2b%ec^g! zmFXrndD?1VC922Fhdn84l1i_B2CnylrX?38b`#YmMr5~2zGmB^M3z_Vvn3-k=~Al7 z6I9a}=Cdd4FEcDwNjf6lD`H7iC4b{d-Tskqm7v`0FiYJYr)O_pXR zyu7ptl2bB0%A9#L=4+|Cq}lT7WnF@V6Lv1>OWe{Vr(37ewibE!DGm(rI|D@5X5wd)3m?r%h5J ze49v7giD4ytW|w7nr$KMz<+Z{_m}azW4m^wo~BVXWcPb9jc4q6)kZK^Eq0R0M|(9g zjY~^aKTYPAOpO_GR48s6X;7_%b1JIa=O<4x1TjdXUMVMDW|fwtSpGS`nw5Jty!u&r zEryG=QjV$;;rnM!%c@k-YYnSt!D+vJ#F+e7dQ*Qx$9-904+``6@qZO=x=tljRnH|; zzO#SI^Sx@an-Fb!;#At6BSspzWv+})l-Ops-gC;nva6QgSZ?^1PxQ&S->VrOU)Xg7 zb;z9Wx1oj%#r>Trz3q z)r++xBJLU46`6Ky=YKy+x8`T%tVBk>T~^hOJXI_eRk)kZMP!KyW!jRoF*dYnma6#Q zFVLqOZ>peH9I9g{obt<0*#1J_Z~LT<%wo;OCJ8biDlNn6DK-Grj$p@euKOx_t@Ezl znSk@6dYs>P>7l2$;v|WG;dg)LHLrQ#^Nnj-+xz$I4A#ZwrGJOMeO=s&d%ewFw%I#* z@>?4#{JQp!Dq`S&)~@vUrJMHnO>OFZN|h z_9G)9M${%IUw>A9t6cTUNu%7Im&ac6H9OphE`5|NouIOK{e=ZGTrGtyeyZ18qZqnA zo+@lS$R)ZUJ54UgTCIK%EhTn4E-8zwE5w>s_ZK>0*_Ohbm*R{!*PM zZ7iajq1a$AW53G&jQwZ!JtPm~Wxl`>=a^*ueU7+a5}TxPC~W8?q=)nu1>|p1hJ?(c zNYzXONs6~1Z^79;m&|A zK+f9!2rjc&%Gu}RJ~ou7(9s^e$bm43eW^ZK4Sz$@z7StC8fph!B+R7a!+bX^-&X1! z@O@V2I;FCP?$y_mK7##d>bkTCx85`{^Cj#&jINb~Jm^ia>iL-6&~ITGnOk?9pl_j0^{+z%Ik{-90eD7|Xlf)ZM1RHb1C z4}ZEI8qq<=rkC+AW}uGk*;+(AUB2jLvp8@5AtB6I2X6~mbHKu}3>p*Hwg4ScC9B0l z?lxsRMpbxpp;NZ#KD1Tz84$i<3F}^1kw+3*OKQgOcrRlQR zRFi-8_p!Yggtik>*GOREQel=snvPy`1{#pVLX#wt^8Bq6BKFM4tO+mP?MimX%YPg} zmYeC8ydy_opZ$)ciR~$$J5?_sG2^BrK4sJqllR*2dF^|2l?yn40zlnm+Z3qBaq0q^}OKQeWyEYlcfY zCJA&;n3ndEL!Mw)aSLH_aNZUv)_*G{2|H}+nXKj*G!FEW%tz{sDhWv6Mpg);nhLoH zl7Oyf$LKvh5|?Z9L*ro3FlZVRWNQ+qiX@e#2L~>0o=$hhbrlSFoX0jvW4fvKe2Sn- zpmiyb8c`nxpEgmYN@4pmCT>nPH5INszUY^_LpUX*9ta%-1j5U&C@`o*vwxvjC2b0= zEY~9uTcNuaP@y!CveZ$NuL>)ZC5I&V3KRRpjy<9t?iG&xd zFNmL)ieW;ggr}-eRr#jpP!B6Ra)a86=_|77Ev!ykrX4LDBqWlly943*u{2H3Cn-hL zYxY~1Rv$2n(lju=$(~H$k?5I9`K2JKB71ur5lf)yqD(5lu>vy)=YOpL{b<-FKekD2 z^|5opxMwf4dR$C(m1jj(_>sb^@;q$$DnGdD!A$EoKwg*G7S?!b2rSN!l<41d^JJuZdraG6YJ8P}ZHv(uu*F@KD;#j2esFI+u+$Ekf) zm&0_|-9HWxZ&KHSCxyEvqz1jQLJR0sAL$2YdHLaq?67U z1{&D}D#YO`!GCbnzq|-H%98wezR))u^=BzbSWEvnq>FJPrwP_2scQTKG&uv(i2NhB z_Q)zfz17;TbQg^fxs-!N*{eo-(dyAfA?NR&KfPbHy~%HoREST3cZ`Yd{1TjJ{ImK}L<^TKTxwzueQi!W8@DuWmH z$UZ!I`+s;f-s!?E_z{Gy`?5-6Ts^dD%HY~Nw_vADMd<0gbqn5Y&T}kmg@Nzn3~3h4Z5I}jVq7&Ywr#=PjK#y%5#H)3 z?e+-}pKPSjds;dZq&~E#<6!6z`}T>VC4vU*bz{9bxVyi-YLhBfZdfGz!|KKv$L4PI z5WuG6(&~9zaVPwxnBLg^GP?FyZMA=7c^qb}n!IGZ)X3T=dZ8o4vzF@z&MNEo_N%GossRb2kxQZ$)!y1(9r=~#nY{$T=Zl-w9#%Er4>s2=rdmYV$Ln z|B1lPt*!mM8iln&zi>=@s$%D^+Zi4(H69JOSvJE^PyW#tG6Kmo6s+4G_kZhb9J6u8 zzHsKu2QP4TVe$fc`%$mxx<&8#@9fGK?NoX8DwcFEeeFfNRhGSK^3BfA@-J+TdTf4v zcR-?{tb;AodB)uw69*@>60nL^Ek#R-RB&n?pN`9&6k>)G{$EShu zRiH*I$Cu!5$UX5^Daq*CyKwQ9y(QP@+_Q?NL)x36>bu9UuHUrVHGd>&T|AD9lP4Ae zEI}&(XB&apyu!P2aMMnxY~@0=-XJ2B^ziF{>@{ZUmLlcO`YRSIG%sEWAC}G`61BVx zN~V>nydOw&6RF;U9~`1x4X4uwQL#F6K#oxhd4RRueiQ!refWm!6a`ZFcCFT_cYgXO zPj*w+wOj3t*5aunkAI$6{EK4ZRd)uqEE%G;8{3hLN*yC{N_IqYw6Q-DG4`U8QaL4i z)UxISC!H6LBPs}7;YMIP4p-q`f2}bW>>TUtHyf>P^Sbe9zG5YI_Djdk*qkpJW?Qg% zvU*BI%kh{bJs@f>CuP%a3Psir*}K$((zT-!=>P`oT^)F2cz-)-b=GHdVnOG2qFDkE zB>1nxV`bbZHrOjaw$bWdJhwHMT~|NB?wdTs-&(&eo7+5daiz6&=j5TwI^0?T`5p$H z;dJ>fo#oA#CdwGKfW zweBARndn^-y?ydY zh>TMmZXlhCLpm@S(kFLSWTrZpxMK@p(kw5Pf|%59b>iB=ny@?et@~uVmVB>hWp&b( z*RHs7$25al*{WGoWF%zOjZOYeSbeeSO55^1Zj%vdl7BW=wVkFd*3%_#6?h@alCl=m zEin!QAw>|X~ z`=h6xx_@;1si&TM>M8!zkNwHrc(Jqe_7Qqr)-hhnA1BJP3(dmm!cRcX_S;>R!N;A{ zCV0+bTf5(FseV4)_E?-BTwrt>vQ}er=moLvG+E%d1%KRerk~KH@zp#Fnpe^%c8&XV z5>4WBSj%G3sdunP*SEb>6K~_eRT93o(U#w~+ke6^9lPp(XISzT?{qj>g}p2>1aI5V z)azgMRYo$UQ9r%s7xdu8Q|h8Lc` zFIupP3r@apGk2osfhcnL8OL)fcdkCUdgFZgRi{oh+uV5dX`zI<+BSy0jSXhranZ5c zhkv>h{O^)Nr*M6~&PyauDioMcBX_?W7n5@CpiDL<9G3MY(M}16c18-6@~Qcab-m7r zefv}GBT8(y$->h4Z9vk;W$3T<=#( zJic2A+kamB5M^-&I|@DBx>?io7}BunWU*ZPF>i^%KG#bQL%oBAe8gxj*KipSVH5UW z!g4vBl)%;lwVt*9v-S7ZcdhT+rtR5t#A=V&`}Xzr z4fadyyX<@H`|Q`*_uD^jKWIN}zu*3t{kZ*q*0&(>mZi)Fs2NaI=fcaZi@397Ak+4fL@L z$TK6Y!vla^s=;)x0RDdqOy*lBttOorX{gN|vei{tks^=1lxAn3611h4;?8ky2k2U0 zEiPZzAC=Rj*Qd*OJAY#oozSEB2o)yw(^0VMvgz}>P2g-P+=#jnv+2-Lgu-PB+=5%9 zF49|%)Xt!fj!`<8C+&{Xd;x^I4pBDHxH8}iPP40xI{KdBwD+iqyr0T4+_~ZAnskBw z&?R1wKZVK!VAwbtjHy{zF$;N@>MDDKbhITqqimGz;uKkH zV*oqiSqZS%$A5z{Ri9odMvVv-0luZ zw=vDj7SeHO7seb$6QNnikTvR%yr7n=E)sQNnv|)l$Y$?SPY+0|&lgquxjKkRtQXn1 z^$^jmU4I40OWn+4tWRZR)b-tD(A5KlK8D9O_RwgW4+lD<9-9$>J0g}$FSqMBIs7gxjg_B9q-8sGz2bHet*16g%0V_G-^F!MG})mpPce*qEsS?AzV7B zp~MMViqz?mYR(pgS~GOm<7{_4>X(~i5<5ge33YVVEJIX*DS#B4y(VJS)cm^DMbHp2hOtaX82PbQn_FEXP0S!g&nts) zvKAnQdXTTgE2xatD3#PL15heS94%Y&ZXxDD1Ds*3iN}`Fm&6_bptub1!m*+W zl8~~YlLnDlOAQ^D;tTYxnh4KeY+YXG~c zA2=LP^E1^VCQq#e6Bu{E=rnA%{^({Te@RK{^(7UE58_$Wv%MkF7sOi&ctwyjyCb3z zxt+U#(llt1Fr@(_0@2I-$ajioP-8k;pUD*JN!4r-v5)u2)Z%?~R@Mj0z<-cxqUj?| z60rcH>T=f9wA$a%i9?Yy6siSs6!yV7sZ>3OyL}=);uE{nrMr4*0L^J)R)Da;VRv@n z^D!jS0CFDDEeJc2-CiH{A*~Lc`MW(0^{9sY0mdzNsTBAEYg&F_8PYTjxgul~u_!XQ zEdjD6m=$qDtT96Z#MKEkCV#Bw3)40&3#sX_4*5!K2EWKKUBfi*0QSg_GJ~78W&1{e z&1PoNZ7#OTxn)a@KVnNjkQi{aCUelhS`qUR+rms~x9AEl0uBuF1tFiVmE(lqDMBB! zxZ~4_8rQ(VR4y|Bg9cKBQAl4&c*eyqx3MAh_@fTt+`!dJrBUG(wUT!Hz~@ zVWy#v!c{=#b;DB}M}KHPf&2=I?i16KVu}TX=V-4Eu74Vw{TqG1sZSfaF7IdTQTAKN zvY^|jW{54qh!}g2E;XW(vb>|(y;c9$&-xr&<>I~c^(BZvqLJ6YF2Z!FxquHEF_sqT zgl=FNRWpp2GWL1K-pe@Mr(IZdQbaU{O8p!vMsFWxZFITDuYb|E=>1NCJxB-taJuiz;w?A>(iv(t|HU0r8-y;UG2T$70I1R(h`Q%+QaRS` zr*toIP_JNlKf~<-!-P0%iGm(x>>YWtK}GtRn)0bxr|8RZu6NW;U43DS_i#QNJdH&y zbaC`XwfGDp4S!UJgfktSu0PD_cw%^|MeI-M&M!|(hLh6|a7=bTr1##$MD zwA$4K(^0v`}dzA^wDffH&f zfdhtY@P8a4YdTSw;;$oSuBR4vuz6skF#;@rJ;id6CN_mcqqqM9?lp*2HbI;0e{r15 z@jVQtC#TO8nxlv%8r~nBP9LTNob@1A;ZO-jBQkWEhIFz3$>ry>T7U`IzmxX|+^`Vv z%T-Ae|0lQ}c_OoM%96n)xZVuDyd8``{sPl(>rt3*_)iXe?JhhfK8)E>>h?lq9iU zWr{yUeW|}<#%?9~U}C&qlcQ8g?y}c1)BFY9!yT$BUuGn{s9G)dE-J*fNebE6gHaU$ z^nZ3kDBFPV8_~P1;px4Yqzd5V%|!5Ki@8LHo^tH>!%68Es3nHtj%}D3lGQ5lSv^lV z^F2&4YSY6u1jR2h*1)WIG5ogmd~0ibHnyh0w5VDjJi28El|`UJ8TSM9wKCKJ z#>-ULB&4ns>jJV`nhBkPvw2e>IVC*Kh<_+u%xcs0Ff@)S7Fa}M83ZI*6nZcbF^^EN zp@$Gu&SQivL7>(Uq{v9ER+_zrB6s*QSSXmXbY5N%c0iKfn6Gf4dh;?6zeArG zJ#*5?BVwRFu{$wU$fz#?jXO#cJgx&41dM)U_f0Yo0X5!UDB7ivDn&E8ze*&kDSttF zUR(z6bra8y^jtJ-?$9#~%?T7QE~q8UV884J!oy5(gjF=cl1)>VE~(E|Jj2S#UHM^P z#K^0F>8iN$xFE)J1%8!wR|%QYP3=Xv-*&ra;`MVF91OwjI*~4cRC_vdn-Irp290 z`h^)NQ&t2Bk#s5FiyDztWRcbGSRpZtH5I58iA=@-->(uOIiWedhcV_^x3Gr~I zqctsfj52=_5YA!XEH*I=9!3aSo8f>N5ocFKL?OqntKlk-I4ZaaRoa2?s&K4?8-b+) zV$AeZNE!>0(tzKi=EOl+On+rjC}=Knp#8y578x%jsj!x=vld9EBz9m@M{=EK$8rH- zZ@4bgz_ger%9YZUT!aC26|=BGhB;!JA`0PF&sZdZkicy^S}bba4ADd+AB76wc5gAY z2+Ui|aAqEbt0 z4H25Ci3icVqDg5aN69M#OW;ODTQ{5uE7*h(&T0zzz z5v8Ev^Z;PWXn$_Na};4FB#W4y znL$Wg1w9H$2?ZCKa;RTD6Y1DF_&m)~k6E4-!q168W`A&(>nK}2SO$9D)VL?GofPy< z)A2x=6^l{xwZo@rIK(2104gj{QE;!+T9bxG#h97eTOL%X%|oQc1sEVSX$V!OFC^90 z@R%&>nmG*pX#%Jg#%`T0!9=y7lJi_&%QfYh{V@BPzj0$~Vn9Q8oyzp0Jjq9BDJsTC zV%fZH0e^anR3c$&qYUaBNnmOoi7J^}kRU#$R{-?G5d;ka(@8kA5vt`6pQ+C_R8EZy zs*TCV!!A-o51ye;joks}S|3A(19E#tm+;pmM= z>>Kx-znj&+Z7JzlW$UU=bG7}(mG)`dDIZ&kt$)ayJAP$c>g=Cb-8Rmo_F}O~8r)A! zo?!b&kABsSNB{^+#I=R>?OLtX*a^x`SV>EPl%`c0?^e2AZK*w6f{7f@^5=eA^b321 zbA>w!KT&u)_TLVHQ$jX%M9RbJ7dZlHw8u^F(EIK%w6d>#1>r%P@s*N%-7KC!8lCLZsr!(n@AU3qG>NlcqD@n&VDXY7_x( z#{KoqcDwV&wP3EZexd3`rB(BIyOTs}*)5gINtl>&BQ-2lR1Pt1rwu@+HxU_|C3oHw zNf6oI&C(?K6juW`2rRAi^IL4?SCY(erGKNsxzL1fAE~KDD@g5f?0dF{T()N54Z01( z47TszzHxDdDs=0aX6zW1ja@HxLNXbH_{`>_Z7AiGI`x)A64*W+T6DXx(`Fi${bF{f z;{|BedhC`Y=>k4!V74>!_X$N%hzpBAgq=A*>Ymgd?V{K}U^~bSI$+;E`{quUvwyP( zC(fUJpxxo@+`(1fY^g+<{OeQ$MJ^nck>{bzXZ>6NYS(M}D2o$W@=^qt24d1QOd*|yhS z-~`i@omsb=O`iO5`g{BhbNkK5AK%`7$V0$pzL`tyy`%=kC+DewW_Z zhYt1+&Y#%WKiJGaGhQF-t@{QJaixgYK}-#zWN|XNv#*bC2G4+hqUJqd$4u zMJ7JPo|s(v(B~d~wEF0yAASF$kFqll-FwR_+ZaA^YW9UW58v~W9=#_2;o?Vc{WW^s zck$w_RE<1V?|QLMHP|j(M}M>QjfIB`A1i#J@E;4$!Ow8&Dma5z>|>5@F*Sw4fWH^Uhgm@hq#aZlqgVV6E`(*RFeCQ z;e9mCL_3X??slYhDbr4@C>`s2T<5=O{9HEpnv7$bZpNG~>+E_rbp{ z-D8JgZ&+86kBN6uI`OddX6;c;%QC+ZGQSthd?784& zDd`MW(c4nvX5<>~0)J2_{#KQ|H8pN00k2#Y`E#aqJkW{OPLLe3v^hmeba^lGVs@qL zzOEuSYh?f=C1esn6(X)}HUD%W-CQWbm>bRSR4f=0Sd(IlVxKIwV!cL+uPrK7YoRrN zZg8K_KtRe3I#*LkDX*o_~v)6lAK-){0r^($mdW zIQJuvtXvM~Hj7$AQ`~0TCx6bD zCV#R0y~}>@o1$CTE&NR3v+y!SY6H0cw}NR9zFcR;MPS~h8tE51D?LQy7PFqI#aOvT z!U;}H)A&qmo+4%yu~JRsc|jDf_M)e|kcvnrcfdB%zJEAi18mAPWxKpPENURiS>g%p zST~wxaiC-WlT*4%+weQRNWI^uZnb2^=&(?G5##j;{=<0q=wZY zQeSKt>x?CIlZdfEUnL{0$ngHyh zr!3WU3}>yQe{T~V&ajy_#X@1HaJuji@yGWRe!1`~g%5uKp&X$N>ny?GDU4OIH|ig9 zl6^7m!xNFnYwEf9!>{&0hiW6=U8_l~tqHG^EzQpm-JIwRM-5nuc<Z>MeDUIUCjW+UciUZRi%1d&W7U|(v<4$p`p&kK#EyvRNg@U;D=afB@L}-O zE#fAvJMusNg~`pN??(HW{I_FY^fx-iFl=jsvu85YQ_fg9JoAMT10h`8&C=TBBfX<1ToMfD4{o|9Yb;eNjmBZT z{y%^4Zxvok+EwmL4b2o9IZbBz80nmb(?E<|i`7nNc}i*$tR-0S)Ei#Oyf9K7@w@7L3)mlxG zx^BsEVN;u)3QX%DGO$diGmT2pe4m#C@D@=c{b?bOiCAZ&(io7@X|8P`y|~WHmhykp z@@?Y?QEq!3j^wK3o1w)!$96gIoLFb?;G}&zCrMnW-d$I(-TdykRijZ|=opKIM9=@* zNM<}=Q@e~AyhoPV$-=dTGldId)Gr`nR%vu@(OhS-|C&`2RUqoW8OS zOqr$*wv##_x7%uN5oPa=d(>EQeo}u{mZjK#k+wDYUb$=@|DnI({y+MAD_2O^GF%{& zmbyGmP9*6W9+p@f@-Swl-?1gXN!qr&^l@qP*G%5Z?n<=h2fZ@wF~N5Amuz~Nok}K; zm4C6cTon%eiRyAGC^A-D_cJVwX8!*Ni`iIH0001ZoMT{QU|;~^A6vsSt;2Vq><6g@0E_*GCX`G|oL6jDpjWRVT z#>L8To&f}yfS3yi85sV9`3wLoHUf*Y--9IqSoNFSHW0mcGVJXoO`0+@_X;U9GpCGK zSsL5gl_i~EA!TN6(|@rjwkRWp2E|32G8O-JdYRfB3{DFcm>DsDqh3ucmr?ZExe6)@GjoN`}hF6_z)Zo z1SAM8I`mMOU0umN<@&@G(BYr}zw?;|qL=uYd40zQMQn4&UPk z{D`0MGk(FZ_zl0~5B!P0@HhU!3H-Z(V|!UG{gCA7)KMWOVNnWg#k9_Lk~|#{R??2pYT%K! z_a6drFZ!{tTDB=DYkw+XO{+R4oU4UjhRF5dlYWPsHnpUd zCmjz-a-IZ_x!8`du9CR}@&`z&X#L6RGzk@HwNU=x+U*A6m5Yq@P?Go7R}{1|aYV5v zPid&zH3fCcXDU}2waQb&1J$dbwTr6F7|0i`HIa2HR(bcS*?;UT>+I30^IU3LIi*9L zo$L2>+4*E*(IxFhJr`5WO;_}M(BnQ&hKw>@sCyo!q;)=DP?ejp)5LmO6nz|YtvX{p z$!(=|3eocnvCQ;#R4CKhGIO9rG2xQ3nep+;tdZ6eGE-$jIZk zXuh*bVnz+jxM4f*-IL9aOfr)srlGVVV=t}bkrz%%&75otM_zBa-bNdG>sXdu;WAO< zY+`HupzPSKgc|zHQtiB*kcd_G+IGyPu5u}JnNOCn&wn8I2T*$AB95}nxCdS3WSv~$ zQ?tw_#Bt!7Lar{T2t2DTG_eeVYhJAtI|>xzYCPrswM7=oP=(AuTvoyP{AMe}rKXjJ zs#TOi9a59Ch=o~KhPi6vR?Jf4D-0~=fkvAdi(xI%BF3*001rk001@^&1IHoXk}pl z0D?#W000~S001Nhyah#QZFG150D@!y001oj00K}(ivR#@Z)0Hq0D_bN00Alh00Am) zMN4#TVR&!=05*fb0000V0000W0&NNrZeeX@004vF0003V0005z-bxwFaBp*T004ym z000A;000F!FqOXtlL!H7f1CLQkQ)Uiff_*s08+UO)Bpf@obA+wRvcLvMd97hfd~n4 zcXxMp_YmUl?(W2h3*)?8ze%P}?RkuW_0=u<@4GMbss+#ms4q+}RH3e&N^wV}t$4Lk zzFtrF^7U$^wR}%Cuh%+%{VJ}NT#KKp<+ZMOZ?z~k&`4Lh75jS7e?(7u(M)gp(3gJ2 zT{Ha|z(58um>~>h7{eLCNJcT5F^pv#)F6YHnEv4#dRy&*v^h(-N`O?vxmLxqlNvna)5&z z;xI=z$}x^}f|H!$e>7(}%Q?<-fs0(?GFQ0DHLi1mo800yceu+v?(=|$JmN7=c*--L z^MaSWD*oa&Uh{^xyyHFXeBdLW_{cJ&s6?r)n{}(WZ?(w2H6r`gitJk_ zvTwc0&wqnRW1~o8lSpH;NMnmgW2;DGn@D53NMnacW2Z=Cm&*6-7U}H~>FpKi?Gx#> zsC@r^k#?&{`+!LMph)|WNc*rz`-n*Us7U*mNc*@*fBS?;`=m(wlt}xuNc)UP`>aU& zoJjk;Nc)0F`=Utul1TfqNc)ON`>II$nn?S)Nc)CJ`=&_ymPq@yNc)aR`>sg)o=E$? zNc(|E`=Lntkx2WoNc)LM`>9C#nMnJ&Nc)9I`=v&8ABIkS+Ip>?mIp0Oj`5|)7PmyyvM9%q5xhDhMi$2P0)*n?mTqp&fgRt&26vUy7jf&t{!aLHc%x)SGvFf|8Ou6nb0^P$|9}!t9f1D`X4EW})PH8dKoqHZ09y|CAXzIF34FcI8RMSd8FU&&HLf_wxRPX_(b@cb);C%cK| zQ;X`VryCWETb>dd@L|}BXL&5NJ%tg%E)?(I<}}sE4r$C>BTDN z@GPdNj7Ustvee~8BZRg!tCoq-7voy9W-PJ%>#?Co9FqbPV~GDdX%;jX!JdYC zPUJ)*XM}(0rk7s2>Gb^kY4q*!e{(2CJ6FfQ*6X30Rtq_8E#Mmqyr33_g^LRXZ`6yD zPQpjMPBeh=gy9_kU1h{1My=P4f^0r~L1Z-)_%NVJYc%LZqh62vTovE^Hd$wP*TzN`oiqB+K_8OsG;x7BnM?d_?uVzz$D`8Kok=-gxm+J zT*z*M@mNC{r-kTcNoJ?Je}vp^Q$xRt-LJTu=!Bp-Nh?Xh@Drxy>Zqq>|}NriLjhFzM(FM%JfwLpsNiN|fo^R0@JmpJABmfQ3E^@t=!yHFY;QCKMip|XI~)Pc42JnUETTRz ztzh@*f4FuX^+iR)p19H*^x)Iytw9p7gyGL`D@t}+J9+J^rkY4X5(OEg z`v#}Lam3RQNHjQ*98Hn>eatbIg3`h3FpwvfsKgD-SxE~+N16-Vwx!{Uv%GvA@qB(@ z`%-arr%dqD!pIh@OJWxq z0!<;zQb8bMhKZ*jSz>SqH@P5MQct45@Y1f{Q=dZ=3DJoaJ20_Qi=p$_(F`3^nBn%+ z!98_Hk>9^mH>M)O&s?RJv7~Cs{1cJwMOgC84!M~sh)G1%sl|0_Xxvzo#k9&fuMoBH zroyijey8wEf8o!NhbpK8vz!UlMbt_;P~$p`MX zF~JfJE2$2Q8}n#;xoQ%d5r5f@!|2RftxBYAYDITacpxGK=8l$iiFzK8nyfIBtDpd7 zNm31&f4HD?pgwoOWYqx3pcrEPH@p}D&48Me1#N=nptDA+RAhup7<4k0ZCTQ}W(1~c z+T@rnk@+dmLc}7K(5?Wf$vO~{T7gri!&YOl+4#4yw?g6ZzQTz_bq{)k{W2&T+MU|9 zb-6@h)iYd+Pc`De^A&^X{BUy~n$bA&2bPEYf2!|ESR*#5H5KX+{G}UALWWAP0whjr zC6eUtgFPp3iJw7)26B_QK~2iN?-)a)q?(lta62({bKwJl%>Z`UQJ6cJ>tXH-% zf0D`|ccCy-bl@V8qj1bMNj=?KXf~G`Pcqk(4gn(kCeL?6^ zZvoU&2G~IG0aaoHt-pNV>C@}$>!>uCf37d8XXO5oh3<)N_r!H4iyxaFpa-wM8kNTH zMklUbpB^TaxxHI5tkv5x+?3V$9}07Y1BJt&z59Y|#e;aJvq4dtIPigA03ME_j1x8i zR6F6xC>iv!caLXp5a8mS_@fS-|5kf#J{c_@jCaens`_T=h9$S|bh=((?yABAe`XhF z8NVeL$A_7L30Ku*RNq(kJXLiM zH+|1G8&=V(cWMU@wz{o@2lFqS?&*r|<+$c^_(z2ZewZi`>@9j2EWk0aXb(mbf3CH+ z^9%2K*DrMTHCsEAod*uUM+;RPfA3x2f0yfCR}3$I@ba)Is-oGL?FXDh3rZm@R123u zy+E&{0p&s4V>}v7hA8A|hmSzrrxW$WThN&x1XYvC5WpBMfh9oLbcH~KVa}knDB8gf zs8Mj|_~!2;`vU+gzij(qy_UAMe;TxI`7$QI2DE9gFfI?dL@Pj+sV!I%e~6t@phd|j zZecPuS1EQi)mLoIq5NX>_2%V=?PuoVuP5jOv9cZ)E5)W))Mg%}os+SoZ*Y~m#&SBZ z>U@P@90Tq!Vo)&%6kW}4OKkneb<-2N2vmR3_(@??-!I|+TG&$9QMg=CZL$fn7m$G1 znHCVhPxKK0^#qGU0g_}oe*=Jc2DybKZ)eb*!N`E)+qXMs0J$L$Cj ztq^jOs6?)C(BOLEybZF2Y$*hVTpu>Y?wXl0bovC)ZnKqk96*e{CJ>=R(rC~4 z>X2OomKcuqqKBy`Z76%o(fBt@iG&)akFG4XB>b%ng_JkOS7(T4fwH!e|kcv=2)9E3>po_JDU)&c={pw5T^J&_nPHEFKQOkwta7#Hy1oz zt!kd?#on7FbO+&o`bVB*a!)qO#%1H*y)34|)0K+pDxUXdnzKXpVc|b5%m6ohKv-ZF zuv!X}F0_B9-n*h5H8|Tz8Hg|V0dt?E)I-0g zbL<`X6LJ{}66<8#wVZ*7sD=eQ=amflt;_~YJ1y}fL6O*yTpL`Mfj8&_n4<|Umdu>-h{WXcQdJMP;iQuLxj;1tGXT_@J- zl+cb#4QafA>pAburG|L7*v6%1^zBSQE4% z{)PDsD7=aW=zFPLkyjSeU`7$0v(^W-s9FJYWQIj_$ElVrYrw#8#cWnb-2t`#9PNC= zoj0x_f4ni=x^wONTH_}GTeijo(|lFn>R$I7+o}6StW|p79x)l#h^)K#uAgCv53qcMHlq8Pvn9L9CuOSwJ>60 zc&bQPzuC}$z7C^&VDiE8z!XYa3n+6VWY(}Cf9l)`Gh#_fT8w7`Q;;+q2s4>y7vY9% zP|*+6qC}#yT~~BXIZ4cljTbn2k~m73IvSMCF>jL``%KGy9R(2zfta>rT9>GN8&@R{R} zY&(wLg2`S`OuzAdN}Z%gsdU>lRQe9ue;Uqx;?fU|F1?_u6SBMDz%!Q%>%v2U06zh~ ziAFWFh5=4FFcE8546%vH&%6etFU%I|MFg+yylLRg{blXH%wB)?aAK{$W;nWPOL+JV zuiH`XT|70sYTlXpOzHQgP9EP`HTp;97Y-%v&Qr%qH7yNR#+Utg&p(mf`)|LifBRDT zCoR8z=oV|L~-qo+5TUM`q!_1eW|6H--_2XCUpBX>aJAP`zol585 z&;?Y8hOr zZ_t=31duZFy2+MOCH4d6Yksjhd;IFCSc$!Msic8bLc=0(RRRVIJXzYhfeXk&twAadgo!rHaY!~0t<2i%h!Q5)lzx)C60_^# zRA#(_+3GXl!Vr3O9d(0D!A3&V<>qV#dW5S33`tQ!JQT#8I2h%|Z}t+}N$4*@Vt6cD zIw6U|NQF8|!X%9oOOjwPe-+)R*}4mc8`Y*kd|?X;tgD<_1T>9gQ*P;Snd&Mike^iv zU=^r@+aNK1RwWFKI9d(KG+T9R^sgOx&R|s%Za@=Q0Y$Dup}Y zxq7Yg|MA$Y5-6$4%>1cJT0CpKpsu{}MrrG2qu}!b_8f!#vQpRuf3y6*WxoVT&=WqO zK{6U#_yJIQ$9%GqnJtc@`9|mkV7A!PV73^$tJCepInWiR!Hp0N=ek|^|NLNeb+8>v ztEYGPb}GyiLYSf@Loic(SJB)fP2aW5rd2fSbN3JCR#xW7+S*`aW3Wl5JIKvo^#PCs zs&i=D>5xU#u zueiH20x9OfD2zJiEBW^C{_e~>-f`r2e|P%rZ+~S~d*1)PnH%1K3h#SAeC6%Ie}n%^ zp`6L-f62rfC43zv9BsshPg_UN#3$mD@IHLXJ9>3?bj~_qeu3)&rJ&jO3rbD9o?>PM5u{H*V9;_i6*qaXd|&-y3o%YTOFZk?H# z8En3%r>D>N>4kz?aG_5=AQuPpD?s9Yb~MD_`p|EFtB>!@^hB~^((P4x2jmCQ5Jo$OMVRE+pz~K00g9ErnBn>MJOV*84mMma+97-De;tGfy1!MVU zFvV2je^X(T5uIu`1~MgF{=kj!Qv@d}b)r$Ms@KDLDxb$(DgN4d8Y-Y>&>V;W0(?90 zM!$?68ejXje=9ywcl;&vrQzpBpBs%oHM)Rt@t{za?NWOqfX;mLPF|N zUGZe6YR28vw>@8}&y-7U%c;b=IsW@f4A4CyMqmTZi(7zDr|Ok*%^~0Zo5iE)ESPfZ z3T7B&r{{r^1oIVxIa_o<7noH}m?rD>2&@1}j=)m!WK+RpWgB& z3KYbPo!OQ5?%dYzPKV00Y5@h=WL#D)e(L_u@e3gIRZ00ExcKqVPKv^%ABb4 zM#Ef^Onh`|2v=bY>Y&y%Lt<%de<{7J(@*AX({rcOtJ(lf_RZK4;h(Yx__1=;X-v6| zT6@dk{%q(H-I|);f6=i6hxgrJC38#TqJs3gXEzH!)t-&Q!3*vA*{z8{%L|vlB$^1| zt;2Kgz@RTMKwkuOFEsgtZ2gN3hT1iC{YZVWYP)HpG>FVftz5bINOP(-e?PS{Z~xl) z?p!Tf|59g~v-3@~YgnK1bsk!ACGZXyoL#)qoU@H!OWCrw^B6$3Fn1;1q&FVU&Z>AQNXP0qo-d z3VOV{?PPRibZ&jIAAjYZwY8TPulkj#2d2o=XAWLEOVE{%KR$kLZS8!0J3t>%XtyII zF!7om?F>dy&<-LXJN#SYk9_1K=*$D-&p!3kQ{&Ixb=O_Q7(XY1f5_jv`CE6~@vX-n zJb3WIqnq>nF;a!z&qGZ@I1Do5F!wcxHUc9p5FwXyt2hXI+5DrsZzi{_2Gu*_k|rlS z^_22&g7Kht%PS_PES`(?fr0RCN~&CrzPYixPSUw&W@bZktGQN@RQnx>GPzMFc-mn?5~o=xE+72dwH<}fj!85e)Z8HVWAIUqC_23iRAk=9%*!G4KwSjC z*MzrRSX_B#j7tqB~IGh9&Gx#zT z@k3EP<9X$?ncRiq6MwpB+K}8TVHK-uSS=E{>JT+nEAs}JntFiZXpJkT1jrrKC*igP zDpdr~B#G;|WXp!;$POVj)6j5T#C{c3vn$l75Z6%Ue`edH1ku>4Nx?!W$qRrCbtFp? zV#bczHKE2dwdpGLg?U4lHHiXUGEmu^O49gF$@ef>Xu{*E2)Wg(vh0P-ycVOg_lC2T z{++ka%AZ3LRRgL>GG(f!yQ=wwhb>Nl0)p5Gt{hUsQRGsMnnkr{0%a?DnfiF;sH3D+ zt8AK~e->K=^gVP)D8$hIj47$<)Xqu0P- z$y3Ib1%7~mxekBKoKv|<`Ek2!2TrkgfnUB#f9}rmP?{ja699D1FeP`7Z=G+BuP2Y= zIbx^H`TzUd_;hprf)CEY&w|cv7xonn!JWwjo7GW}r3b8|ER9J(M1gTTgGJ0~cR2ET z!sV2m7rv5yM&=36fQqmp}zilG1z5&J!!>aqCT54N)v^k_x+dnq6_MPXcPeE)@}#>64OlfkvuJ}_5D z$H$+oH5J>o85C5xuC}|821I2)aR2@1$Hgb5!by-rS&wkk4TM${Od}w_2^vrYp0l_M z^fris8n6xga{IraOUwpwqe6e<$hWybTw=iNO4qA3}JA0^rubX4f?Ui=iR| zYA0O`8b6IFt~-nk2r)~B+V)_U9ivkDVYT{wAWpzyI`t~X?_jQ=7ZjNR#QzY&TO|rW z4O9BdjLD_4YnMwZ{CaO!gsr`RHe5TvWI!0yNwQd$>_eq5R4RX`5RRazSF(PGf6Dq~ z9QtHS;rcATF=20!UNi*Qo#a0MB+a_qnZVB=hh0(T$rh;r%US%blZb#^Cepeizz!2+ zv4%mIh@yG;Up|HhjB1i93?q%l9$^ReLl-^8vUCbY9oStIWQM*l725_Q zrLbt*6^kOmVnuBfE4HDe?R~8&e~Ik6W%sKtIq=5N_x`h)!ljvxy%o>StLG0KK9voDRe_DAURW zbsvo#!(gw%3@uke@q#O?=095guu&w7~60e-Hu(_u&lm18*-S zRPxQ)zzc8`s5>=F)3~g?vEo`>Hl_h+DCH{AO&$KP)eRs_=28rFDr3_$SU>?7VJS=W zF$@*Up!6u06iixF0r3quL1h?Kz`CM{8Weq;NsLgeIlQHUXz+BdD5h{qGO*%R4QP0= zDA8i+@Uf-3ta_b7M%8|L{ZZ{anY^cJ(s;Qe6NMu8kMP`dB83@=?G+Cx19?o>& zIjm4GNF_jrY2RF%|MUceV#5s85LB?dVM&nON7UI9O#t# za@k}hnW{#+e*sswjP|7x!L}qGNf{xE8)z-N91RjlNM;4PWH2FmV_GJPLW)og$~j6if@X@y{2RwWIFdIQFTe`rj?m}jnfK)OPve+u6RIzK8T zh06-BE4&G`TnhxnD(V6&B>ZO_2i#BESsW=xf`MSbb(9A=pg?fmy_K1ZnM{Z#B0kT^ z%mFKi6QLtBNd!XU6bMx$eV8B_1fu1hXjLc34D@TRM~D?7Jr*|-1lBlnz8pRV`T zdbJvS)bW!aIB-X6Dja|E;V*sc*z7wFTz+8hmf5{`99pbTO4NJj+j0Y7w&}v*!Yu-^ ze`QgZ3Ei<@VC0!;vNJ*SMU zZqPmGdkRtO9Jq!9hfsPdYCAqgUkcM#ElWg`T}~~FhA;)_tr9S5+-+b+6waJh#Zo}= z5^UHYbqL-)4IF>;W%n=$)6Nm#Z_g6Ye_Wc~0t~5BPIY@!Mo2;pq5!s2yn0Y8_mX0( zp3ZfXT}iQ5ZADXt20R2rh8#(wcx1{Q$JIkqBOpCZ&+@#Y1Bkt}R|26zcP1hgEe_(k zA#=~;G8h4=N$s6Tp4fA9;#|;pbpM??z`K%(64b6yr zhFtks5}7)E&Wmg2d(h43Ls)v4e;Ld3h9N0OnMz9Sc1h<)DJ6z6yKKQ#uIE=TrIV}d zQqFWE2$oAQj|^=9A|F4YfQXh*z3dg~!)Uw6nH)dyFpd23_*T!ml#ug2DFWP6_*mf) z7|q;yIdMe+lI#r9i3`4;EKdwmL7G{}F75Pyqusulwn$|Q>#T?N{Hzg6eB|wJ}71kn)d1X+ES2s?wKS}teod(x8fYh&54aKZ^h@tS-$w+DzEY7% zRym4V@F0_c7H33q1o<0kAl5H{R`)28iRObMGCZ()6`6Q3Fo*D^2h>Jp45g^=5gJ&M zN1>?=(E2LZN~*5vapDqLp?Yi=OU+Wn;0r3TC@5nhDZq!yoT>&^f8YW{F`QVd4BX&f zPh=AQH{|VrjQd%P1!PB-v(H6LSj=21Rx@-4p(nD}{Y~xC>1WYp!AFCzGwg|+;>{Ge zPUi2(BL=+)m?ygj-^L8Z))uq`6ijNV1WU%7J^^DEHd-rtc4)$t4cJNLR?awJE)rrWDvK9m&DW(S`b2Ot{9cuU zUE+euHoW3(Fow`c_YL9igDf!$6|k>o zU}Op)Q7z1p!FJSJ1ArhxObHOW$oWn>TpS6YJ&T5d9*Nq=V z2);K?UGNh>OWeYhg@Sx=;E-)v|+A`REd9Fkk1RY-% z-^=~cvQT|~e?3dx$+FPJhgcSstu2^%pR)s?0f6vq5)1-Z&hRb384_)Qc`ucaWRx^n zCv_$qA?VJXB4-)qir{8Z2P!8b+eVTla(tuSjlFic8M*+&bq5p*Q9whq{E`4yMC7~H zHQ-(VZoBIezgj8+rV0!X1CaBWty?DaM64RX4A?|Mf4GPwz#`W_2*g5=%ZY4q1*<9` zV`x48y#3=QV)f4yHQ7)=*T^;)v`p5_YGj^K6b|T_F)jm22Q)703s?zvS6l!#nr1ti z%qCdvo#ZTVi(asSQ>ukDkH1Z zbx9&t-iVa~LN^HsIY>LbC@&cRZH(F=wtE54sQ5;Y1L_{0oP8rPS79pQgS@eLCW_7! z+c&D7cJ{#T8;P-C1c_Z)-42lUsl^x1Ek26Be_Fn={HnVyCgI6Y__W}Qkg5BpviEow zajND+xDvl5D9YTPU7@ClVx*`SS{1-3`ads5b^7f5-SXy z(#-m{16#t2t~`D4;=_IGN_E@gpS}Xw#l*A6|A#G(-c{mgTZM4l|3t7#radG2;LF!{<4{4A5o_Bv69KR0;oe=bl% z#j4j9S$LkJO4PBH0o&Rht}XQ6Or4+x#`3DgjkHbC=x5^hn%Upk#5p-I=sVSRC-Pme=uq`7n{xTAEIxKSJ0<#-u=tBqV9Otc62BDasw_bHhyP( z4&4=uKY8cT2ku9Yjvqsne@%I`M798}bqo6omu9+1NTS)?SgE2{1igh!N(ZPlNwv$; zYlWk6)DnrxodFyNFwy}3W`X$!oz2#gc{<;`YHPFFJ$2|r$kf@)sX zetO@I?YsBxSl_?*_GAk0-@XQxN@lg3iR+5$RUW@Ksx{_n4Q%hSHW2g%C($c$ zip(n%=ot(XCUKQfu6h6u0dFgk>hQ?4O19zKB^?vV9GJ4p6v-l_Q7+Y`kfxpRCkcB=#0!BIp|0#FCtCW45hNbr9>h98rQhTI@7{ahVEEg^;FyP+N|TZ zN*ZMzC_rsSf71bnD$QnVy&NhWfHjZL#(I&PCLoKl$l1X}v!?3H-YlSYc&KFS#)*R- zl~r9B5o%R3+}aejWC^QrRo_sl1qKKwk`MhlZ&x)*v*2eqBBLBXUk@0W_s72hc^#O#A?kIs{^mCe>J?gvQ8R5bvZj2KU!Q!9E_uI z!L*yrKRS;g{}KN_V40OHuf@;S`+?D$CC^S)8uYXE%fdCya}vnfm?dWCIfdxj<5SHI zYf4k#;imIZ7wn)~t&*I*>>BF72FX*YR2An;*fH`w)87j7#24cdzKp{vm^ zqIaPO(WlVULb|Qy*{8#NMW(>3lcbwG(KiuMS>ewXX%eGBU--ipaZ9kPupU7bvI#}@ zb34|LT2-;$4DKC)&J&5Sd2OIbLr-`U07?xPe{tr1&mWpxNGbM%D7RxblS-mV+$or> z1BL%lH~a48!JOe}S)|LtaEpBRsJjf)z8vQGt(}DdI!pNja_c**bU8~15aDLfW5OKU z^zqeE&X>arIV79QuMo?dkp0YR$p$RTi-^)JbS(_*Ea6wUOwydw^SJgP=UMpoMGYen zf0ZABUKDKs-PmDE%R=xCv;Iacv6_4|=x~+}gq1R!ObF?2Zn%&KBP_Iz0qWtmArQC? zFqb4ba##-u7oj{t zkC-z4Z6E4j{O607t#YQ6PtB|(D^NKv%Vtc7SypTZONv4}6$RjvYe_J6v(R3h2Mwp$ zpHZbU6yGC3zX`M+Oi)QT8zRG!AdbaTaM{$OFIPe^BSlyQ0kDib)5chre}LS8S0l|w zMV%;&60f2w%;teB!uLd0Y(tgjt1DBn;y^!TTd#o9vI9vB6_KLrmDqC-#-Ug-C{U_x zNZBe{Nq9P)sLB-g1&P5dyUA2Lghq)OyoYHp>~(0%HfWwyHsS1N(1hqXTsL%UjVN$xy&c)f_D}_HVe7ErTh5u1_ zvGB6U7vB_B*>cTbGq*GBfh+{2lZ!eKOdPB)5Og`u3Nhkmp*?XVZ2wWO)8oCt2;kai zl6EFSlaq)g*lb{aiZ>hzH+BfJw#ze@UhX@9Cv0?l$Cj?2NAYCxAn65m50~1XMDit5hYaePTEy#`5kq$!q$zYeu| zQ_ww3%#ub#LnW#jq9;txLWIy$m0e17S3wGpAy=3aS|tEke}JVVz+Id(9WBhv<2l`M z6zWKZB^#>Afr&XajFNO*Iq7z0Lnp7i9Cirr$7#t{A5_>raPT+z!Ely$R8spza_+q#rHpDtp%m)t;nW?0P(4XzO z81H(}(kU`cf9Zlx8jlGtj6^nsmnE>I{S2wVWG8vL?fFP|?nY)|wt_OdE}YZ-q}>X{ zHVgsS8v_A##O4Os%CvmTmVp32B5NjF^O9B5d%ht8i+H6M8=)tHy0%%X36UX2KQ!Xk z^8vO8gj^r%p+}_6tHclVxVr*$03sMH5lah{#Pb}!f3&a~$4s|mX1Hn!CuG}iYp$VN z8ja)C)2(*0$QG4E@mi+`an}Izx$WCBcf3?}4Q4MD$?C#Vc0JIn3$_Wu{|HuJERdz= zXW23R1!Q7+{R@%D^9<$xp~wO2ib9>vGRka_{>V3&tt-0 zCX&b-e?)=T8}y0>{JQg1D{IUQ= zYf_%20cLJ0pigG(di{LO4HO0&2}n*5Ri34kh5NEiKC;VcClhpmaNY?o9S(bIBA5Z< zITy>L6z`Mv4;P8!Ur~#tilOQ|=c|6v4Fg%JsbjsZ{nBi@?&JYEX9#G&|+g ze^w~<0zCv~o^j@X*A5oTdxgk9AB*7B2}j6XS$%^-C$ReR-iqG3fdD|f62|rF{rUjJ?fi&aBM3)V75{|+mC&j#k_R3 zZ11!I2DXRI9ebJrTg67eq-+T&)&XG2763h2xO06?lBp*F{7{VO;_*{D$Q9EB<4wsn zl#&ci5%fz{8BtUU_o_z@^h^=@?(RFTg_)y`Kg(nI>%GTEeV&^O-ys?u7++sn+AlrQh#7PFt1xy!9N%mgJ^i;vo|n$q2ZrGg~C6KoR(O&QQc6I^BxIib$@ivrDBZ z&mv%++sptsRjFgiBbZm))1?Y1$X^ivQ&D2K4A=^&7k+BGJulL0EFb`)f4Gi9V(wA{ z^dJaZF!sHD?qXf+lOi6dKqEMpxKUvuKSk^YMJT}g56OvqO)ntG6D^~F@&UTC#>FxL zMJ5xusnos0i#k+H=a*{+oK+Pg24zjnf$8jX!eb%Pb*?!&sB#y2AD37|BIU(}04fxV zB#3U*BuRxvtN=onDUU1De|{TXA@qh4)wT-7DTYlaHMcmhN}yw8IjoD|2@$EeBrwIU zDhjY88^2~l*gHfhbrLG@@Or3*kJRJE1|qr$ya0#tn623xR{4670CD=fzv?*zR0;a;Ua1qD2hU<6=w{ePR@e&^3ZFn8;!u#=Id>KB8ufV79tMO~_b@+Ao27EKV1-}`; z6`#Xz!@mSJ+1v5G_+9u{@qPIH_(A*-{t$i`{~P=X{7L*6{tW(o{0I1P{D=4n{3QNQ z_$mAheir{{{3W4Hf4bSC-rjJq4GrM9y94!Ck#HIIwq>y|uugkoqpcBK8P))wi;cAW{4Sr(;uKEwvY=hd58x{NZ$XoT77N!=?vi9lQ*a0TZi?T{mWBMN z*WA1&_BTm{w>UlTHC}}W0@@gMoNP0-FmUpXy9UlMg4574e@94wfg_k|81+DMi1v5; zWGE^Yn}?KhqE8Pu|IM?k*VnD?`P$NK}s46y#;e(FoU^)WH5Dn|OGpd_a2oi+$porKo z5PIC|4^Yrgf1_Tf1?}ouNYuw#JW9|Y2z{QnwHNXZTnj9*OA7A~dmykx?39iAgJ96{ zg8?7)yL{;L?6c#yMtmebA}$JuLko?dSkh@F6pezVfw*OlFYdxW%&p-LKL_%hmqka z{2+)eb_+0M`f-UqHF1wjUKl@5z=9Y*FXRMBF$6et{179FM9^p!9uQT_OjCHx2M z7O+AN@+&cVGZ?z}LkS8N&;~r}Nh(467FL?$e~p8?!GNZ__Yw3MuARG0JPn}@G6%53 zI63|;Xx3v`s!d^ZD9ho*=)Gc8LTDXwx@fCNR)h+Ls*$s8Erb+3PSCvA)@J<7iDO6M zu1gOKgXxgCeZK@VL-xYYi`K;NU%gXoL6WbHKu)|mT5J(#rBrUgjZ$3{)LjjRI;2AI zrxj^JK@%UD=yS-StdfB)S|=?yPgg|(HQr0eJ{S?YMze;Xr(i<(_$cOvCNXrKNL1u} z{FPW2)8oNpp~DY?dLw^H$EMKDt2E1jf0Q;Tx?W7{#|15j9a*HO;0`FNJjn?Af|%w9 z*$0Kb<)Ii3DHbO406I8+n2YC(-_`(vlL7wU4fKFQ@(@5yK|#0;BlA9}?5}V71qnZw zc{wVqLPT25Q&?KhJTx!D6F;~cOGhyRD;~{^=ML`^d5PmXA+obh$P55QK)Synx@Y|m z?tczMf~GBQ%I*{{LLzop0JW79z!DR4@r!VDIgB0tUSs@?6JlbHA3K^60Xn!Zo2={; z@DdE$+SB6r-{(`DeSSpTiZ9T?1q%3STDXB_hth-)#KitIEllD8J^arFD_i^IG@YI> z7;v`8A?b@2A2hMd=q(j_If|os& zmeXf@+pVHOwum@eNZb@YvI!3Ggz6Uga{vVb+$%PlMZ3lq)lpJes=)uR|9|?Ui;HuM zi|Cf|sp|&=^zqKN6{X^h-@3GX2HowK(UZ-^hHEFYmBp3C%Hl_!`K`q-eB*{2@Yd-c zr7Po4`sKq%*rOHi!aAfw_+;UT*cY#pFH{IG_}j95Dzi-l#E`D#I~_$q+P{!loVn=I zNhqW<$qy8FJV&S{_IXk*)_<@RSGDb(GcjScg^&q%=g- zC5bIXrV1rSS~3+|ak=k=Yo2CEl=AY0dE&q)Q-w}pwXm=7bHb_-FVVq=j(FUmec-pqezzi1Nl&VBt6blc0{o}M}O$bXlQAA1DEVmduDH+}r2 z@uz2I@Vzh1&Aj}T+i!p5k-0B*Z@wM=9+^8@_{g)s4)#JxLPEhOr z|B1Og`78X@Y!5{d-4;%y4vD=3*Y+dlvZ6mz!YlS}&-h;N&-;BNk-mnb`2yzND2#sUwYghEFALAr#Flqi8iv?tPOJ&cRul{SC-f(-(R>M5<_YKur&hGd z)V=Ot0S($CvDIe2lq_?AvMAeuE=v;}3b#Vi8w7n2P=7<=DS$^uX{VoL0ySk5Uqb#| zFqT-B1hTmag77?$c}XfO*^--apeJJEZ(EiD63Q~ocJubN%!+3+Sou0P#IhMBw73)4 zs-gK5%xL146s|iCfF=M=V3Si$O~5CdRQp}PN7NSgizGr_QZ2<(gOU-rz)FUxxVj>Z zTM`thHh=3jcZ4T`BiGWXRWXH^Ol&zvC_^8D=SWzs=!OE?+EYZ>4#}zrzQsnlVd%1{ zx*TN4%jn~=5eI;!0nQoIT)&?q0Zu8F3<`ftY+DR(u>w|OwhUrUbAY21fK66h$+Lk4 zkWoc{46IOB72ra_Rlr*CFx|5W3l&~eiHJ>R0Dn-pQ-FVTF4I$_?1&(hq#^}-(A-rO zi?j-j2;Ugs4M}VzuLGdgr2#aW>#R}aNbILk_h8hKM&wWyMaA}IoPsSt6w574_O3CA zmf?pg@LdOV*kYkq7>T^9mM|zoVY7m5o1#2V%4cHtoZ!Mol!zUNF1+!RKX#XQE-!B^Jv$g2hW~E&dxLNg z!~4Q|n)}HC(6ar)j>4Y8m4)jHg;A1;X5p9xV($ee5p)cs4pf=tI1kpypud1SgI?y? zYH#*{Y#y{hkci|65mV~7_yE|i8}LCEP=5ekcRaH$_)QWq^sl~Qs=yeDGmZqvz z=aAiqEjz9`hun&)=r8|zrJZhTr?vbG{o5OGd*5Kk+pc-XYIJGqL)TwCDo6F1U!6KM zH+}7J+iN?~>ROJ+N-(SJIt zX}Ucfe`R_#?W}gR?R)oZd#h*dp1;Nn{@u}vDlbQs-gz17gWNd{V{=>KZn1BrmnXmp zf8=12sMVP)ChF!<`fOvJAm6b1{AN43em9Xw=E+>_X_5_SmgJu;v2A6i+i-4_XBq&Y z1sNgqBwb2+c{)VChmQx=xA4pN^M5r0`Hm^y1ST)FlyDA9ZJS!um1HUffEzVQ1wtl( z29#(4n54DQiao|vLM6tDO{SW)27Cd@r>@ZzW=&WxRvjl+neGTSrlE9Db>CE;VvJ}+ z;>4kq@e>G@OCp~@l_ev1006oIa)k&J0yq~{44uPMbWg$uQ*Blwv(?s&AAb;qyNYeZ zRkxLpEjs&oZm_(k|JzM4*65b63fgr6XHTHV@96&&d{c4lk4o2eO@j2Tex^ zL$O9rcm)9@ZUW|PD}S6$mUBAUEid1LDvJz6-B9eB)5^j?A_Nt-;bIVqYyCXSG*Q_2 zGj1zd!BW%M8mDs@Pd`gLViPt-jH2t>ScX9Q-W_+DVhOgA^oquz64B?qdK!9A~PfJ%zvt^%&h9J?&_-Q%If>5 zb4ESWEzM}8k>*0ufCK``;*$VNz#>iq#+EpZ3?m)&_4X+&&$KOW88*9P!SW2D*_JeH zD-<*k=6_+^w!(di>Hk*N{ciSy`N2=vz9lpAZwvBP*~;!`j%o%ye}` z?K`?YxBg^1(3x&%%+@r64`)e*a*O6^$JJ$I`bKpjaE+LFmlnFED6A3 z6HBC)o8kq!X;D?I2`$_yPEWA33Q;3&ASMDhcYm+q^XI?$SBQLINB{CK#i>6yLVwdd z3!xYl>ZDy9C|pvwvG5-XKcjq9X@>Mw*d8g}YTHT{)9z?EjZ_iY1^U6zj;1Rbaw@|& zkv$>zDDGhv#uGl+D}p8?W1k8Y!FB%C_58(A5T^^NZIvrCQOP&WcztVqFyG$pyfk66 z^MAvcv})SrQmb+CML`_T#W4vdgjfD3GVQdHB*o}xr^^=WgSD;ovR^9A2F}HeR;g^8 z)pTZ1pW!#qW%F@7qakcu=kLFiCG%+-*d_fa%{iu>e@x8OR<`WNkr=3IHnivFn>wv>9J&~clx5k{jHT+m3vrL8-KvB)tiee zjfF7OobA=;J@>~hoywSwD3eBIBle&(&|{A^290k#@-249_~-et@h8|R_Oma)>z99U z_AiUq6sBYMZ{Zs%%(Uq)g88ci0$n>aA(c%8MIb0l@M`_Mmj^_l@=2|gc-+z7XErZ^77e;|KDkbB^l zY|pbxe&9uw>SA-XebL6j-m$%dyTilVhmT&fJiFuDexp79-qK>@;!SV|!5!LJH{CZ>whpojU;2kxTpn!2gCYOJ6OLJoik4kpR9=quTx)xA-qutUTkD&; zRcls@b3zX{Y~~R6GnowiO33HNH9CE+SZ&sP*9&-12||OJrp6s^ZGW)WUk0WtnvLSg zliY-(R=8oos6^btBvyd1p`;<`e&Mjnj74*HLbWgtZOV-GI&Dfdtk2w?%XVE zUmWN@026|X+ia%hg^Q#tNS%~pSJzGkL&k1JRTrzIgsb-%tc`P%F?L}+q#*7tl+-F| zA?no`oVBum1BP|bQ7EWIdVceV>=yPr_jB=d_QTIT;~Aejdw(@O#G5{Q%VSJ?`sW^d z#w(xk{%gk0)l=P!3A3T_TS9Y7!YxVF@ zf2QP+s9kI%cYpZhFt8o}_TBtcGIa7&`K|xnsa)$c4%k_}y1Tq(iV)7KOD)S2wr6;b z<#^9)EQ-(AL0Iy6@a<0Hp!Jidl0xX~>eSm8E}Tl;<*7BV^ZW3nyuI-%LIC z^1|y$t9w`BKNmigN1Xl`fc6|~)~w|_v&E8aFyWDQB@J)(iP_;9q4D#N!r7s2 z+UDF7>VGbBUDQqE`(YKILSNVPj~R&(S-x-R(eqOuyKhZFKL(o6BAwfElYU3$qCnVZ zg+NH0tI?=fdPz49lAeV17LOm+q*5g75nPc}ge;jrZ<|dkmObBG_h!n&jltf5TG%Si zrPXe?U47zeXqM?BHh#!#U?r-{^m|;xs2Nmx^?y@vR~IxbxhSz~s5TLzp=JCH%MvBB zydsY+XrWH0QdPb{HH~0CyTbY^!(x>|N5tDjWKdPfcrvbn(Hla?H!mgqQ)3dSSr=mm zO-iL6_-bx#ijZXQZKR{!Rd^?H|1T9DDtx2xox=Yv{Bz+4g&$${JWk5f*`Y{fgO8@( zOMkfoRF1GTi3XT#N-4ih#l0t3y`3)B9rhz*5W69BSn@j0)wm9zXs0nW3Ld355%=nf z_D}^OjrOszl2nIhlrlkbN~T-sQ!l`LEmf5?U0%JQOOSBFYCnC5T8iXU>txcFqpUq5 zQXZ+J8ULt33fydYF$L~rP}`bgMiy18Wq+Q8WPHM%he^3wt5!=XDf`{pLOq=&orc$X zPLw2(TP-boQYRI{vxpQ$u%J1ETGb z7GL94({k5>Cp;^!L~u)%(w0>seDB0@qiV?TMVeVO;ppBzppE~LUgWpvxMx(@T|z&6 zc!}$dT?u5>b;y*@c8|H9TaCA4qD@yEOWHHUNJFR0rIv-UrFZIGyX;A;YI=>uhG%+2 zpNxCmn&$GkZCg->%y^FDSIZ%GMSm*Y#64b2i=D9O$a`yDA-Z+L3#{Tw7?iBGE;*}a zv0XH+s1{X&1)XMIy;zGw;+~;ZF_O%(|JrbBURq8|WaL|ASBa74Js{dUv9A>DP(_dFY5;e)&o?e>ZSLN-)n66s=kI>> zil`NJyX)I*y?gZN_tut1$-WQtgQWi+1Cnz)pHLJ3(tIBwQyEBdov1splLjhwPa(=o z_&2eA*66CBDYg$hiK#e%RDX=&q{Qz#OYDF;F|~_8K=iC4S&pNGRam%@Lv%rQnlWe8YW2NvA-394Nt$F`AwILZJAao2Wy=uytRYUg zlb{H$9$YexFMHbjxVB|zmLbf)OuyFHe&8E+XY*0#NL@pXCzeSHj1kXs1jUglZS>#-uW$iL z?@@q&xu;x91%T$Cn<{3c)f}ks-asv>XM;{35;o6kL*kkv%zuJ(wdP@_;?`RIR;QUM z{2{t-ALwX6hqFGNfPA67A>2%{l(Wl6J!~jZp~D?`UIXC{`%*o!8V00&Atq%wP)@B# zR!GMO`EFRgt<>G)yR6PtI$H%DtIj8V1bevDbx9Ylut}`vOW1knSt|!dP@7`uMG8@7 z(C_Z3RErz=l7Hu9Z54~9=mj-%SWCgBXM_F*L%{OHp%AGMk!2GJ&vLwZT#s$@UcXJ{ zNVRI-f)ZM1RHZ=*ud^;1k)dOg3z!j8P{H>HJ#YZ_*=G!z*sk=rU3Fs;GfqO{(+D#$e!B(lH(vEPvlu^e z6MZ}GO2AuE|D6{1=Ma4CjX5gc_+b-o&RgxGV}D!TS0#S5`8OlDRLMzSpy?AoB5D({ zM*3(IEABpNkG@LBJ`dbi3_#)iE+?q7!-{OvNefQg$9+S1_y3ou1YnKlyrk%xyh+$4K;H*8g6QBG zR}>gjqS-(!lQxA`mg}L2%)nXksZbh7qm;?<*M*rHC7UGp5)-?`j$NWKxeRR>{)toXgJ>?9XoVBgr5RuB5%IS zKTxWRHWK_;mZcjGQrZTvt?5LLFEy|)&*h&GLpf7chvIuEmRdBP) zH`Ysf+w9c~u>sFfPY^#Z6@!>e30GFbs`PZ%rXH47==ilI-IHX~n^>JTbt|0Pi+@QZ zQ+NBq^&&&pU5}&`QLo->VOqUcFB-ar=}q=z3@<)cm(nZwaTS@fqmWnvO&6(C0k-Mu zelTnL=ts>ed67kGtB0Kv#$9W!)#YNMt2`po!cP^Rljp$7SNXwJ_oiCM9`cGzx3ESN zLtuW2K16?$&8rFS*VF=4?nxxk)PFUA!yinJ<~QNJQhQVioWUZQ&PG(TBiBlbCdd5I zR_3c#EZt!F_)W)lW!+euam%CZ^eRxgn2On3*j}wKZkgud{QB7^ZdNUi6ve8$L$w%H0-i8T?_ah+@77U2H{$=xPP+OtRU@q zFw8DqsFb8u@r1$$Z8&C7ylA!+6E_;nG`woG=vD2m!uFgo0RSqlO6QBI?+bIG6onTs z)&~VR+>u~uAfm0;@be6Gs+*_};gSxq(MaOS3By#Z>wJ9kavHN&v|sw}lUF>$4`*gx zc;=GVJg+HBUoOU_B7$%~ynjX~?D#(WnxD$$uypy=QN8}MGgqBD@tkt8a`K=SH;R=s z3rm`|zlOu#M>^?jp|8+DphE1g5)6jD3yW~WG|rFb3w?uOZ<-Q=wbYk=x){aAB)PRg zsv3VUO-`RQBJaTUU9!rLZ?rZmop~)l9$SCj=vKp>aQWc8FlKL^J%7GiwA}G;kW`3{ zM~!Y}xk0SKq^X}SHjepMEjcCOy-EmQHyuGd!FZK$HC^h`CZ;0a|0=^lep?zVD*aUm zxv)zHiIms93I@L}HB&)Vyb2pWw&l>N`)lXGMClgxfLIM5PR###p61zUT0r?#A%y{- zPWn!>v%yK18a+NH?|**FKX%h_VW!f%hKw6KR*|RoQfXD?t1aIwF1;?Q4zJrX&aR)i z;mqa+y=?G>>P)5o#4g!~M{gW0M_V1Z(%y?uZ_lWb7?*di>xO^%%^L=DH*nF@=;ib@ zKN0^Kp%v%r zW|{tDCtNtZe=qjjnT+$ShnEk#_R^B=^4j<-tZTdO>#F-djW2g?0lySJ78>+@cd+(X8&_ z{Jm;(XV0@D%YRGgx#wN*J=29jR1S^ZM@A);w<(n|^@BawVj-SUg)=UD8z6o}{TU{-aMblm_A6O|xPWifW{Ys4i(}}OJ(6xhjvu#2pwXN9x0l_Z@^yHR-64>dz;G^ zsbb}ZNq@pWsIHx`E$)PO1MD~|EuXd|w}UrD^uq2i!pjfUmU{;lM?uP}@l!?%jkJBF z8`wfTVmh7=ozSu)n!dLimKEAi3M@YD`$)_Zk2+nrHqWv$@mk#%Jh_zV?mIeQt9Y_? zl#8BCHgR!L91K3%lf4ans;l#d=bgavuUXyfc7I>?PFp{G=%vrTF)~beH}EvRq0)@k zN;H#2wA8CEH+GM@ZWY<&wDy^Fq1-87wp5v8zB6ATbNcC*TrvKr<%K2B;orS#=j5f& zzOq4sr+w|YjgP3o!k(Hl;eLpzi-;70CxZdjLHeDWz_qvIWH+dx3p7fOfr zs(+~mr?To=@0Y^J4H9L3=JP)m*txZopI4)>Qs@;9DSRXB+;y_S9+RWtV3Va&4Da|~ zeIO%{OhdssZRCX?MQoI^FP%8?zB8Pi89#wup5+!Dr|3TZm0kR#ttwBS!{Y4xH=eXz zW$AOq-_3rWe`$T#WwWzu_iXdonf3RXbbq?*zR=3w@^9?sCp`HEZ~P2)?|SddbvOF- z>8C55L;RW~`a#y_CFv#fIIdk@z-376#>)VGf ztzWa<(G1eMcoY@KUzqc;1T6vFtoeHLBKPY4HCutSqyyD@jfl{on_v0U&({;D6iR2> zUy)d%dGSp6uuLYm{4pRerBvnpK!2K>NYoa5e;*-gFquAxBvr`uIj$?@0oHbVP589; z;7_d}@(tlxwOUrse)fe&JBj01t@c`L{@8(YN9O;c7`xT2zGaj&(b|rz&Cg#_^94S+|-(lJ!IOF7=?{SYe5D01fu83f9rw zt)x2bvl%g`atqNc0dNp}zTvSlY7`snnIB$jb>thO2_vmD^lC1i8nIvM^L? z3_e-ss4=YDT+o@ZnvsVm_x8a*)GmoyE?49aNHJ}z0!ZUmDlF|4v%58bYp zB~`bs)yOhME+HwaMy7?@v5ad-=V6l$OosH)Z5ir`3c$@QA#|GMrGJtik=iYfT;5+1 zR%TtlOSWstbBktLCtZ2vqKmh5-LI9+nn^{5!l*it&fg5HFVY>uGCh}DWJKzu%~dVC zX^GWj!CeN%hBOVM=GRR@1!Ei&=$XIg4<^eki zZ1~cylJ$#K1d|L^BY)IGt$V$|{<;kxUhe8MH1tljWYi41s)f2y^GJu?_W^r?IQh7| zd}-Wi%XozMI*U8(bq_ws{@}p}&mVs9!N(qakU#j-e{?IJ>?FN>ke(NGjHmMZiL$Ih zvv9od0?653yCYNhxRcrh&sl71_c|@v%ct88i}HhWjDCi!ReyLEYC)_sNme&%!5?>& zsyj4kd=<}v<`wsdUE?~H#F6+M*0NZ5?2YW)>ZW^a?5^FlOv2aF+Qy?+TWGp%RlV;I zN}l9d#>pz|rm-e?+j_WO|GK-Bz9ZP!w4N9E20zgiG@;z^rch+d_U)msi%Skzb`ajo z-nn$_m}s__mVdsixxw+!IE-qw+EAw?;B6(7wzE_2X%4LI=gA`N=t~e z>YE9iCxwGFD)IViuTtXCtx{Otk)85x*kNiFb<+h!2b36!(eW6MrGT zE`Poy9@FNvrnanIuAS0;N_(#M3hhqqwb~oCk7ys$eoOnb_IujH+Fxnk)*XFAKdqnD zuhV}?e~11N{bTwU^{?n()4!pATmPOxrl+xBEE>zk<;E%F$;J)FPaCf^?lR69A2L2- zeA@U!<9`|7m4ZaGFX!Z{+>~88lvl|o$$w|d=gU{h*UC4_H_H#o&&l7H56Qoh|5yHt zIb*iX74wjJ)V#vH+Pu+xmiZ#{73QnWx0~-Vf8G4J`C0SJ<{z1VYJS~(#QaLd}V}Jd< z^?$5?v9%mmZ@V>+aFvK#O}RbnDZMw72ph@0@p4TVOF0VX64MD;AC-r2XE5q0zhivG z4r*(Z5L3+Pj%fhUUpVZ@axWRF1aA`RRCfea+5jLZWaJ<*j8eg{S(UZ+co%g^u`Ar{ z;B;J*<|@Ykn_hi&ECpK2Q0edh?SD38f3jBq|Gznvh@DK?R+D}iDtO8*veji-k|K}2 zlxEhKG1}5iaOEhs15_=r7MHK<56em1?a}Gm*$73)bSpkYg^B%S5>{P0xnHLVY$}Nh zQ8!{X868FVSQ^7ExHaq`weV1G^?T?Tse*Zu&M?UrK&a~wWdn^X1t#AlyMNa(Q`Zb8 zy+=*ty~HTPof~ehaR-PA9pVM~U8qceY>m?Xh?<2Jvw(M~9)ppxZ>A81Kmpk#ow=o! z9ESs7AkHb34%mmpUKM)hVf>@B)hpcNwzfyuTEiWYN@3K8ninS$xG2P35f9W<*aQ>6 zLO5cAPRgITvz{y?8aB;_kQEx~J8C9>BW)cD^1N2#zf;MXm_Zm^vM?Gp|XS*y1 z{Z=og0i}u!F~Lx7XN#oUh~{MrDJ+x=V-7uu&@5zV8fGLfsO7SQL{XR~W$G%j**nzJ zebValMb%!e4q{TP^nbuunr>zWsHa0+q*}V&r}4rrn;MN+T^P4I7%oLQBbBGL1n;9qNXW8X~&1BLNesnt6!zsEm}lz7zL5YM{`^@Yu#48cp*-UuATo zR^?qxq%exNJDA~sl>|&&cN8~`4myT(*^x0!$i5?P6iyJcsL@cRVZdY-1Wbe#jnATx}g>yE(S#ChpuM!t10UkvhUi(=s+Ahk5fy z+xdPkx@9;>c7It0a19zlj2zw7XW6hd&ksW%Rq7vgR3F?MtP50v!GJ5kpx>p5iDFW! zl2m))WKwjt_SE=hbo*3|4`z>YwIs$p-9c52p-!a9j488?6a$?t>Rf7ThYS!p(o_^4 z=O#uvRCh%kGbHzf``DnHjCwiOYsP6GFlREG!OMhrg)&4z9o*v!5oPq_=5I}_B zhNw$UD%&&x`j9X{?cS#G4LC+EQ+>QmU4RMz>zJ7E2xFjNW2y3^Z7Os?x293+5-Tz= zS=5gceod501Tlb12Q^fsh9`A;D4Wwoq1F^p^)TBW4SVJ0h{O(2P)r@2Hp>uIU3(GpPTB%2PZ#o)cm*>N2T?eZG(logHdLY=GHa=!BN~cwFO_{7bKvEG-=>PiQt8B6$~Z9_brNbq zvjr%i;Q$j4yarH}>V|_pH9wUtV)E2lFo979MyH^z)mtYe`AbSlw`Wj+cq5)gJ=+}+ zeSbl`wSe&hNwYm98ZkC=H&B`eEfS_QU_>BlnIHK!aSv)tru3OqqMlUE77_bshfFQr zLuaKuunY{TBAOmj3lR$-sxGHZMXS9nmADc)E1_B-M_v!ClS)-{xYHxzBR;WBUAnE7 z2GE=)W(5cf9Cm9PJ|6=j4WP{t-GZ=Vqkq%wp+2P5!83omtKbpkfZxfu=`56dPhd^U z^G(grb_qg>oLN{dEQ1A&`|SHK`m^V{?EOQ4pcn0o8!YlB2!=OgQFw zc0nB;7dO+ToHRxa___|Nu31-N(|?p1$`Z8lR4D@8k*MfH`pM2gZ#s;ZbLK{I$=Eseo5)O{+9;C{i}O@jxBTXc7OW#6ht7= z$SW`up*z%EK<*3~ON#V^YG4^v)3m2D_IbwM&NyACTv$|6J~W0({TwPrFCSoSbh*NO zQJ1L&J2lP|Bp7s#_yW&=VmWluSILlM_j103yDYJ{(y`A@I_h^+o$2{%fsk-TBB~RF z@agRcO8YYEoI+@Gjq~Z?Dg0)kilY~*#itl)peiJsso-?=rccHb!%HnXoo z*CUk;+N_yc-UeKR!GGOm>_Lza-S-#hYh^NDG-{OUagebBx6*Wj|0eDyEj7LjX`}VG zOzy^5jj=ny@f3}u;Dm}w;DBK?cn&KynJ7&17ZWpAQ;XZ!Jh0Fh0T#d>WI3=Ao4|CZi@|h_$vuVQC}M$z_gRza!&HE?8h_*}94g^xM20R?Fi0j~ zth{_y3orreGkJf&4GRIkT$U8^f0V0%SMEAg`l%^DQ54LnPfk%$p9Olh$hoGD5m1E* z8*ZP!l8-Y8VM$%jSV>hHaLk_4to*P^!-oRJ`v*O9{+kaN`t*tQ4*op$6qH2Ng=#ni| z5_PA0>D!QI-1E`bQj>caFI{4jkh)T=3dm|hkLf2kn>Pg#I>O_Oh|LjvICD8J+qV0C1RjHv9gFNq}3OIU>zngZkK@t0oNYcW#be?K#jNN zidHG0O3{q&Efa|8 zVhHsGIzlrfxEON*LXX<8V(QnB_MXPTE?I5rB0j%n`W+i-1}r#8}@ zC5{SiLY21RyDDrm=9+IxpBOXU6_Un;q}1T|s5r4-786+%2%3u=2zl_6MV1JIR9I8h zSqUWFAa$#&iJtt~m}od#SDjr2QGm@kftUro!csL1~XJSO5?-7Y16fE;|xe3 z3(a?t<67_;>Xpa`EIZM-5yUnqN%M#>UB^WCdA5R_p(d48vrN87CFmBMp&U#q3yEK$ zw`wj8y(o#m0HiLufvD6pl!gdRRDZ{fXkO8zG!0F9M3NPUxCawH^kn3?G;$W7Gc}6_ z$&Jh+6~LV;4T^`*cN!Dm{b*Df$1IanqnRGLa79E;1)=uVne7qjGCG2!7gC8b#W>2; zcf?3^CBFc7qsv?((qJN6HSr9UkV8o^H8KYF)LmC`m0YXFD4QG(&;ri7MSpxVfY@m; zOAUsJ>2G>8sM0rbJAwn-6AYFng(eobG^sba$~pke848DTX@+Zn12JGK=}sIM@XB-} z;!td%Y&u06;IdK(FqJHcjAKv(mFYpMrgU_r&(p-wMP6T}JA?QX+$eR2W|R~j0SX4e z8HQ~VAGKgLVU{D6x2bY~*ng1QP7**{@`zl3Z6aJ^?Yb5qfq*_cTO_K{m1!YK1<{z5 z8*u?*hp zmr$Chin{XTR$xt5L=8`4lu8HaRvejIT+#|XLzyw~31VmncoA1t0)K~H1dn%>ol@C^ zI(kr7Y@J0Vz?_>TtxbgjL-a{y`T=nj^e7}H6kKGNp?-CBqyy*R^E5|YX1ZnoKPLv6 z!5yxmY}H_C=y_e?p1^jJ&^LA41!Y!DM$K0apSorfi_m23F?AF-=OjHvpInVXATvMLf57V#t z3)dzl1~hcXu1rqK6JrFXq+&cIW6j&3?s~h|_-7nWi#Bcn{)u zTgqP}M;gkLT1`A#Mm^=i(H#xhcWyg4?CtM0q~PD9iE%{w-)o?oI0U4Nn(*;-|7+l}miOh!LCu|98UQre}g z-m*yoTYtv`lP(ul(oBM~S4?lVT_4R_jhvD}x`0O-m}O7>eFEVZqQX2-RHx35swb64 zyD0Yd*cS3>_SvH+Uzv3{JGpn{^vOHh8E2>VF8MFwybvF}jz4_rlB1_j{M=%O_bY#X zF=G#mf9*r!_5)m-mh-E^&2xsbs3kkChQnHomY#WWV}Sgtd&4LXB58x3S< zluG^&kDa^yhVkzCb1%H%EEDf%Ul^Z%|9|JsovWTZ_n~*5JI7Ak{k-dz+1lU>$EF{c zea~%A>C$ujKhJ*X`uEWD_OoZNr)uP}dRK~Fs=;RA3Yw)aFWgi3aN$dZzb-rmKf{Tu z;1pi5hdH{z{#w}3oBCMk&6Wa8lK>G(dpXVpNGeO4Q~!8m0NLe;o&(yt+J9k) z_i-P42~nWRz^yCbs5ti-!|QO8y>${P-N_8)Ql^|(Q99OzRfwqKdx_d=QsL0))<(<%t*u{?X;)=0eD+3@YCX)cF5OHm@`Nwm~`dksl+;DcQVt>Mrz?u}B z7rSJ!73(!pd@WI_nscq$Q`__Aoiit*pr*+MQgxZ>1WqP57GghA27z%~>XA$jY&uv| z@m$m-Kb3X1QcMGf?yk3jnV*7W<;GxUy{I%a$vw8$swCY7*o&4aHD44%pGrMm3aac1 zUGqeVR07>waZHyOL`2GtE`Ji}J|%Cw zf35X>Hv9e7*I2SW{xiNX{)^2YUhsQg7oEa(;pYmUg_kMP0l@vg<$q6t@QqbgoYz6x z3LO_aD_un77Spbt=iq6PaDo%lBt8?Hr-)fatW*=ZQ4qzeyy)pFr0-D)5wMN4EB06) zn=(zKUEUrP6%ge#S%Y${8%{DG(6N8#Csn0USFHQaw^vEIbk>~JXsvpz?h3bCMT(~S zrj^tfe+1}pf{i~|!++`!sV|lhsVpA?S>~}oUnL{0_r8zABKYKezXc76KIh**o`1iO@oy7FzWORVIFO^&kpgdw1C`vwMn;i=(t<6) zZWC)SbE}9`Qd*b2WV>BYc;W^TDg^eM2YRB5^72-d1Z#9Uf`4v$-KOxm=_WL3;}wjJ zcMee7<~XZ00oX@(S*WQP&RV9vZxS6&v6+ZNaQu~T>QyryRSmEkt43vB-Yl1SILIrXNYc&)rO-2tVO(c zx1Zx#_D4Bi8A@YGG>Cas(D2mjLeTGDsJT}*yU+v5Ra5j1SgG_r4TcD)9{>T56#xz}8RnNJDM_hldOE2#po&$Y3yj&tP!R zU~tD^aPMHSALH?P{@H?8sHv>9P#vL`}swq`))b%+^42VZT!LR!6Oa{hSPi3 zT$VN#DwRfKKVJV|`1cAg0rLh7`6BHq_oaqr3XPm5GrbK}9>7T;My|ywr!%~CRR~R< z`ZURbFQw0O04Ws?n0tEmhAMmQMC#_gVYx!w9kQ30`G$X&oJkFj2Ck^PX3EL*ijlVVO>4=QK$3eO%tW>qL$8r@1^PVwDX` zBS1zcxwe6n+$xt@!V}Z8v;#!B?NvCE%aW%DCeIFSbDkYpWpCu9ecDG!T*&S%m#$p< z)|q9kQJu@Q`9iGb{|zKF92-w3-`4+1sNoHCCJ+m!)Y+_IHM5j(=D#>xX~h zG1&cAk5_YvBqxJ8GHI#HllVxSoZvx;MF9^YR{D%(@M{dqGR}X*u=w-GuV%Nz%JYMs zsq%lAVB6|LIyuaa#pCqzvQ@Y7L4G8Vv@U&KwRLL>yood>sBAfE}_PE*_2_P#=&V zBp_9iYThAUVrhAY4;+AI1jMl75y8ZEFb>MnmGE>JI~FZwWyF>Eq4 zGP*OsGukwsG`KYIHLf<;Hv%`DIIuc8I&eDzJ61kAKE^+CKe|9XK$t-&K~6!aLJUHf zLjXgVL^4EFMK(oRMf65wM$lCJRa#amR$f+oR=8H?S1eehSjt(xTAEtgTZ&u0Txb9Q zc${NkWME*J!o`!4g%Se*0*SMyg(U$~_nX`%6x=!0+dIdO(|hmP6sPyzyVL6h!IOj* z0S9P{(|hmE{~L5`{}#TFc~1yEn0ceg)?_oLlm8zlv4sRFGUO;wVjENJU>ApQ1`gv0 z&csoZB!?$|+=QDk!!5WKx8Zgi!yUL2cj0c_gL`ow?#Bao5D(#DJc38@7#_zHcoI+H zX*`2x@f@DV3wRMP;bpvnSMeH-<8{1&H}MwU#yfZy@8NxXfDiEz_V6(nDsTu8YBXpe z(P0jS9vTJ~`*83G7_h(+E39z>pWst`hR^W@zQk95_!{5fTYQJ_@dJLuPxu+X;8*;H z-|+|j#9#Ou|KKG4o#1%JtW{Jn*9f!J3G;o%1|dyWv5HhynI*ZT@~Yr7-!f;xQ_kj8 zj<9E2dz81t=apH_27Qo{C{Hh?yz{G!E5qkS#BOl3%Z;EJSF&e;b`BmqF~{2}H%iol zmqzD*+~`_13AeW3ZtVk8M^42(>MIh0M8ds~a{-YG=E9b!3PCj+R5&ciI^rzEN;7V3 zF5>azc1g%!+^N#k`J9woNq1-y_B?mehJ;j~k#ol7At+wA76+TIT1ZR9s(C9xr3&L(`0{s9~_Gmv#4!D zsnoT}q6U(u9TOB`!KNc@m=+!Bxzs-Er0!VSd84y#P?BYH=6aUTO^EypB{^wNxsv;T zgA`LG7qt049lM&Sft7u9LM;`=#LY(&Wj$MvPI_U=K3b1 zdeGg#=RPw$^3IH4fu&Xml~_@ymYmwQtqI1r#wO8ksf{8{BZS(xUYe;L9qQYtP+^_v zh=)8zWtzkeO>T#%V_aE>HWEA#KXb%?cqk)A&b`XxW;NK<4b?h=4^z(+`BRGgz zuR`WIQ!L+Dwe%Fi*qZF%*fbiY&wXSc*15MXzS;I+t!VU5K4yb2{7}VRl=wxH`K7GG zc8E4(BZnl!W(E`03VvhHG%RH^4QqQ^>qR^~TS!4BSqzje<6oLAStyoN*=U<2WZA@A j>L|WXm&S=?rF^pLRk~(QC+l7%C#0QB{sSV{mSg|`1I3uW diff --git a/extensions/theme-seti/icons/vs-seti-icon-theme.json b/extensions/theme-seti/icons/vs-seti-icon-theme.json index a1f5817da5c..29712ae6b91 100644 --- a/extensions/theme-seti/icons/vs-seti-icon-theme.json +++ b/extensions/theme-seti/icons/vs-seti-icon-theme.json @@ -254,1119 +254,1143 @@ "fontCharacter": "\\E01C", "fontColor": "#8dc149" }, - "_d_light": { + "_cu_light": { "fontCharacter": "\\E01D", + "fontColor": "#7fae42" + }, + "_cu": { + "fontCharacter": "\\E01D", + "fontColor": "#8dc149" + }, + "_cu_1_light": { + "fontCharacter": "\\E01D", + "fontColor": "#9068b0" + }, + "_cu_1": { + "fontCharacter": "\\E01D", + "fontColor": "#a074c4" + }, + "_d_light": { + "fontCharacter": "\\E01E", "fontColor": "#b8383d" }, "_d": { - "fontCharacter": "\\E01D", + "fontCharacter": "\\E01E", "fontColor": "#cc3e44" }, "_dart_light": { - "fontCharacter": "\\E01E", + "fontCharacter": "\\E01F", "fontColor": "#498ba7" }, "_dart": { - "fontCharacter": "\\E01E", + "fontCharacter": "\\E01F", "fontColor": "#519aba" }, "_db_light": { - "fontCharacter": "\\E01F", + "fontCharacter": "\\E020", "fontColor": "#dd4b78" }, "_db": { - "fontCharacter": "\\E01F", + "fontCharacter": "\\E020", "fontColor": "#f55385" }, "_default_light": { - "fontCharacter": "\\E020", + "fontCharacter": "\\E021", "fontColor": "#bfc2c1" }, "_default": { - "fontCharacter": "\\E020", + "fontCharacter": "\\E021", "fontColor": "#d4d7d6" }, "_docker_light": { - "fontCharacter": "\\E022", + "fontCharacter": "\\E023", "fontColor": "#498ba7" }, "_docker": { - "fontCharacter": "\\E022", + "fontCharacter": "\\E023", "fontColor": "#519aba" }, "_docker_1_light": { - "fontCharacter": "\\E022", + "fontCharacter": "\\E023", "fontColor": "#455155" }, "_docker_1": { - "fontCharacter": "\\E022", + "fontCharacter": "\\E023", "fontColor": "#4d5a5e" }, "_docker_2_light": { - "fontCharacter": "\\E022", + "fontCharacter": "\\E023", "fontColor": "#7fae42" }, "_docker_2": { - "fontCharacter": "\\E022", + "fontCharacter": "\\E023", "fontColor": "#8dc149" }, "_docker_3_light": { - "fontCharacter": "\\E022", + "fontCharacter": "\\E023", "fontColor": "#dd4b78" }, "_docker_3": { - "fontCharacter": "\\E022", + "fontCharacter": "\\E023", "fontColor": "#f55385" }, "_ejs_light": { - "fontCharacter": "\\E024", + "fontCharacter": "\\E025", "fontColor": "#b7b73b" }, "_ejs": { - "fontCharacter": "\\E024", + "fontCharacter": "\\E025", "fontColor": "#cbcb41" }, "_elixir_light": { - "fontCharacter": "\\E025", + "fontCharacter": "\\E026", "fontColor": "#9068b0" }, "_elixir": { - "fontCharacter": "\\E025", + "fontCharacter": "\\E026", "fontColor": "#a074c4" }, "_elixir_script_light": { - "fontCharacter": "\\E026", + "fontCharacter": "\\E027", "fontColor": "#9068b0" }, "_elixir_script": { - "fontCharacter": "\\E026", + "fontCharacter": "\\E027", "fontColor": "#a074c4" }, "_elm_light": { - "fontCharacter": "\\E027", + "fontCharacter": "\\E028", "fontColor": "#498ba7" }, "_elm": { - "fontCharacter": "\\E027", + "fontCharacter": "\\E028", "fontColor": "#519aba" }, "_eslint_light": { - "fontCharacter": "\\E029", + "fontCharacter": "\\E02A", "fontColor": "#9068b0" }, "_eslint": { - "fontCharacter": "\\E029", + "fontCharacter": "\\E02A", "fontColor": "#a074c4" }, "_eslint_1_light": { - "fontCharacter": "\\E029", + "fontCharacter": "\\E02A", "fontColor": "#455155" }, "_eslint_1": { - "fontCharacter": "\\E029", + "fontCharacter": "\\E02A", "fontColor": "#4d5a5e" }, "_ethereum_light": { - "fontCharacter": "\\E02A", + "fontCharacter": "\\E02B", "fontColor": "#498ba7" }, "_ethereum": { - "fontCharacter": "\\E02A", + "fontCharacter": "\\E02B", "fontColor": "#519aba" }, "_f-sharp_light": { - "fontCharacter": "\\E02B", + "fontCharacter": "\\E02C", "fontColor": "#498ba7" }, "_f-sharp": { - "fontCharacter": "\\E02B", + "fontCharacter": "\\E02C", "fontColor": "#519aba" }, "_favicon_light": { - "fontCharacter": "\\E02C", + "fontCharacter": "\\E02D", "fontColor": "#b7b73b" }, "_favicon": { - "fontCharacter": "\\E02C", + "fontCharacter": "\\E02D", "fontColor": "#cbcb41" }, "_firebase_light": { - "fontCharacter": "\\E02D", + "fontCharacter": "\\E02E", "fontColor": "#cc6d2e" }, "_firebase": { - "fontCharacter": "\\E02D", + "fontCharacter": "\\E02E", "fontColor": "#e37933" }, "_firefox_light": { - "fontCharacter": "\\E02E", + "fontCharacter": "\\E02F", "fontColor": "#cc6d2e" }, "_firefox": { - "fontCharacter": "\\E02E", + "fontCharacter": "\\E02F", "fontColor": "#e37933" }, "_font_light": { - "fontCharacter": "\\E030", + "fontCharacter": "\\E031", "fontColor": "#b8383d" }, "_font": { - "fontCharacter": "\\E030", + "fontCharacter": "\\E031", "fontColor": "#cc3e44" }, "_git_light": { - "fontCharacter": "\\E031", + "fontCharacter": "\\E032", "fontColor": "#3b4b52" }, "_git": { - "fontCharacter": "\\E031", + "fontCharacter": "\\E032", "fontColor": "#41535b" }, "_go_light": { - "fontCharacter": "\\E035", + "fontCharacter": "\\E036", "fontColor": "#498ba7" }, "_go": { - "fontCharacter": "\\E035", + "fontCharacter": "\\E036", "fontColor": "#519aba" }, "_go2_light": { - "fontCharacter": "\\E036", + "fontCharacter": "\\E037", "fontColor": "#498ba7" }, "_go2": { - "fontCharacter": "\\E036", + "fontCharacter": "\\E037", "fontColor": "#519aba" }, "_gradle_light": { - "fontCharacter": "\\E037", - "fontColor": "#7fae42" + "fontCharacter": "\\E038", + "fontColor": "#498ba7" }, "_gradle": { - "fontCharacter": "\\E037", - "fontColor": "#8dc149" + "fontCharacter": "\\E038", + "fontColor": "#519aba" }, "_grails_light": { - "fontCharacter": "\\E038", + "fontCharacter": "\\E039", "fontColor": "#7fae42" }, "_grails": { - "fontCharacter": "\\E038", + "fontCharacter": "\\E039", "fontColor": "#8dc149" }, "_graphql_light": { - "fontCharacter": "\\E039", + "fontCharacter": "\\E03A", "fontColor": "#dd4b78" }, "_graphql": { - "fontCharacter": "\\E039", + "fontCharacter": "\\E03A", "fontColor": "#f55385" }, "_grunt_light": { - "fontCharacter": "\\E03A", + "fontCharacter": "\\E03B", "fontColor": "#cc6d2e" }, "_grunt": { - "fontCharacter": "\\E03A", + "fontCharacter": "\\E03B", "fontColor": "#e37933" }, "_gulp_light": { - "fontCharacter": "\\E03B", + "fontCharacter": "\\E03C", "fontColor": "#b8383d" }, "_gulp": { - "fontCharacter": "\\E03B", + "fontCharacter": "\\E03C", "fontColor": "#cc3e44" }, "_haml_light": { - "fontCharacter": "\\E03D", + "fontCharacter": "\\E03E", "fontColor": "#b8383d" }, "_haml": { - "fontCharacter": "\\E03D", + "fontCharacter": "\\E03E", "fontColor": "#cc3e44" }, "_happenings_light": { - "fontCharacter": "\\E03E", + "fontCharacter": "\\E03F", "fontColor": "#498ba7" }, "_happenings": { - "fontCharacter": "\\E03E", + "fontCharacter": "\\E03F", "fontColor": "#519aba" }, "_haskell_light": { - "fontCharacter": "\\E03F", + "fontCharacter": "\\E040", "fontColor": "#9068b0" }, "_haskell": { - "fontCharacter": "\\E03F", + "fontCharacter": "\\E040", "fontColor": "#a074c4" }, "_haxe_light": { - "fontCharacter": "\\E040", + "fontCharacter": "\\E041", "fontColor": "#cc6d2e" }, "_haxe": { - "fontCharacter": "\\E040", + "fontCharacter": "\\E041", "fontColor": "#e37933" }, "_haxe_1_light": { - "fontCharacter": "\\E040", + "fontCharacter": "\\E041", "fontColor": "#b7b73b" }, "_haxe_1": { - "fontCharacter": "\\E040", + "fontCharacter": "\\E041", "fontColor": "#cbcb41" }, "_haxe_2_light": { - "fontCharacter": "\\E040", + "fontCharacter": "\\E041", "fontColor": "#498ba7" }, "_haxe_2": { - "fontCharacter": "\\E040", + "fontCharacter": "\\E041", "fontColor": "#519aba" }, "_haxe_3_light": { - "fontCharacter": "\\E040", + "fontCharacter": "\\E041", "fontColor": "#9068b0" }, "_haxe_3": { - "fontCharacter": "\\E040", + "fontCharacter": "\\E041", "fontColor": "#a074c4" }, "_heroku_light": { - "fontCharacter": "\\E041", + "fontCharacter": "\\E042", "fontColor": "#9068b0" }, "_heroku": { - "fontCharacter": "\\E041", + "fontCharacter": "\\E042", "fontColor": "#a074c4" }, "_hex_light": { - "fontCharacter": "\\E042", + "fontCharacter": "\\E043", "fontColor": "#b8383d" }, "_hex": { - "fontCharacter": "\\E042", + "fontCharacter": "\\E043", "fontColor": "#cc3e44" }, "_html_light": { - "fontCharacter": "\\E043", + "fontCharacter": "\\E044", "fontColor": "#498ba7" }, "_html": { - "fontCharacter": "\\E043", + "fontCharacter": "\\E044", "fontColor": "#519aba" }, "_html_1_light": { - "fontCharacter": "\\E043", + "fontCharacter": "\\E044", "fontColor": "#7fae42" }, "_html_1": { - "fontCharacter": "\\E043", + "fontCharacter": "\\E044", "fontColor": "#8dc149" }, "_html_2_light": { - "fontCharacter": "\\E043", + "fontCharacter": "\\E044", "fontColor": "#b7b73b" }, "_html_2": { - "fontCharacter": "\\E043", + "fontCharacter": "\\E044", "fontColor": "#cbcb41" }, "_html_3_light": { - "fontCharacter": "\\E043", + "fontCharacter": "\\E044", "fontColor": "#cc6d2e" }, "_html_3": { - "fontCharacter": "\\E043", + "fontCharacter": "\\E044", "fontColor": "#e37933" }, "_html_erb_light": { - "fontCharacter": "\\E044", + "fontCharacter": "\\E045", "fontColor": "#b8383d" }, "_html_erb": { - "fontCharacter": "\\E044", + "fontCharacter": "\\E045", "fontColor": "#cc3e44" }, "_ignored_light": { - "fontCharacter": "\\E045", + "fontCharacter": "\\E046", "fontColor": "#3b4b52" }, "_ignored": { - "fontCharacter": "\\E045", + "fontCharacter": "\\E046", "fontColor": "#41535b" }, "_illustrator_light": { - "fontCharacter": "\\E046", + "fontCharacter": "\\E047", "fontColor": "#b7b73b" }, "_illustrator": { - "fontCharacter": "\\E046", + "fontCharacter": "\\E047", "fontColor": "#cbcb41" }, "_image_light": { - "fontCharacter": "\\E047", + "fontCharacter": "\\E048", "fontColor": "#9068b0" }, "_image": { - "fontCharacter": "\\E047", + "fontCharacter": "\\E048", "fontColor": "#a074c4" }, "_info_light": { - "fontCharacter": "\\E048", + "fontCharacter": "\\E049", "fontColor": "#498ba7" }, "_info": { - "fontCharacter": "\\E048", + "fontCharacter": "\\E049", "fontColor": "#519aba" }, "_ionic_light": { - "fontCharacter": "\\E049", + "fontCharacter": "\\E04A", "fontColor": "#498ba7" }, "_ionic": { - "fontCharacter": "\\E049", + "fontCharacter": "\\E04A", "fontColor": "#519aba" }, "_jade_light": { - "fontCharacter": "\\E04A", + "fontCharacter": "\\E04B", "fontColor": "#b8383d" }, "_jade": { - "fontCharacter": "\\E04A", + "fontCharacter": "\\E04B", "fontColor": "#cc3e44" }, "_java_light": { - "fontCharacter": "\\E04B", + "fontCharacter": "\\E04C", "fontColor": "#b8383d" }, "_java": { - "fontCharacter": "\\E04B", + "fontCharacter": "\\E04C", "fontColor": "#cc3e44" }, "_javascript_light": { - "fontCharacter": "\\E04C", + "fontCharacter": "\\E04D", "fontColor": "#b7b73b" }, "_javascript": { - "fontCharacter": "\\E04C", + "fontCharacter": "\\E04D", "fontColor": "#cbcb41" }, "_javascript_1_light": { - "fontCharacter": "\\E04C", + "fontCharacter": "\\E04D", "fontColor": "#cc6d2e" }, "_javascript_1": { - "fontCharacter": "\\E04C", + "fontCharacter": "\\E04D", "fontColor": "#e37933" }, "_javascript_2_light": { - "fontCharacter": "\\E04C", + "fontCharacter": "\\E04D", "fontColor": "#498ba7" }, "_javascript_2": { - "fontCharacter": "\\E04C", + "fontCharacter": "\\E04D", "fontColor": "#519aba" }, "_jenkins_light": { - "fontCharacter": "\\E04D", + "fontCharacter": "\\E04E", "fontColor": "#b8383d" }, "_jenkins": { - "fontCharacter": "\\E04D", + "fontCharacter": "\\E04E", "fontColor": "#cc3e44" }, "_jinja_light": { - "fontCharacter": "\\E04E", + "fontCharacter": "\\E04F", "fontColor": "#b8383d" }, "_jinja": { - "fontCharacter": "\\E04E", + "fontCharacter": "\\E04F", "fontColor": "#cc3e44" }, "_json_light": { - "fontCharacter": "\\E050", + "fontCharacter": "\\E051", "fontColor": "#b7b73b" }, "_json": { - "fontCharacter": "\\E050", + "fontCharacter": "\\E051", "fontColor": "#cbcb41" }, "_json_1_light": { - "fontCharacter": "\\E050", + "fontCharacter": "\\E051", "fontColor": "#7fae42" }, "_json_1": { - "fontCharacter": "\\E050", + "fontCharacter": "\\E051", "fontColor": "#8dc149" }, "_julia_light": { - "fontCharacter": "\\E051", + "fontCharacter": "\\E052", "fontColor": "#9068b0" }, "_julia": { - "fontCharacter": "\\E051", + "fontCharacter": "\\E052", "fontColor": "#a074c4" }, "_karma_light": { - "fontCharacter": "\\E052", + "fontCharacter": "\\E053", "fontColor": "#7fae42" }, "_karma": { - "fontCharacter": "\\E052", + "fontCharacter": "\\E053", "fontColor": "#8dc149" }, "_kotlin_light": { - "fontCharacter": "\\E053", + "fontCharacter": "\\E054", "fontColor": "#cc6d2e" }, "_kotlin": { - "fontCharacter": "\\E053", + "fontCharacter": "\\E054", "fontColor": "#e37933" }, "_less_light": { - "fontCharacter": "\\E054", + "fontCharacter": "\\E055", "fontColor": "#498ba7" }, "_less": { - "fontCharacter": "\\E054", + "fontCharacter": "\\E055", "fontColor": "#519aba" }, "_license_light": { - "fontCharacter": "\\E055", + "fontCharacter": "\\E056", "fontColor": "#b7b73b" }, "_license": { - "fontCharacter": "\\E055", + "fontCharacter": "\\E056", "fontColor": "#cbcb41" }, "_license_1_light": { - "fontCharacter": "\\E055", + "fontCharacter": "\\E056", "fontColor": "#cc6d2e" }, "_license_1": { - "fontCharacter": "\\E055", + "fontCharacter": "\\E056", "fontColor": "#e37933" }, "_license_2_light": { - "fontCharacter": "\\E055", + "fontCharacter": "\\E056", "fontColor": "#b8383d" }, "_license_2": { - "fontCharacter": "\\E055", + "fontCharacter": "\\E056", "fontColor": "#cc3e44" }, "_liquid_light": { - "fontCharacter": "\\E056", + "fontCharacter": "\\E057", "fontColor": "#7fae42" }, "_liquid": { - "fontCharacter": "\\E056", + "fontCharacter": "\\E057", "fontColor": "#8dc149" }, "_livescript_light": { - "fontCharacter": "\\E057", + "fontCharacter": "\\E058", "fontColor": "#498ba7" }, "_livescript": { - "fontCharacter": "\\E057", + "fontCharacter": "\\E058", "fontColor": "#519aba" }, "_lock_light": { - "fontCharacter": "\\E058", + "fontCharacter": "\\E059", "fontColor": "#7fae42" }, "_lock": { - "fontCharacter": "\\E058", + "fontCharacter": "\\E059", "fontColor": "#8dc149" }, "_lua_light": { - "fontCharacter": "\\E059", + "fontCharacter": "\\E05A", "fontColor": "#498ba7" }, "_lua": { - "fontCharacter": "\\E059", + "fontCharacter": "\\E05A", "fontColor": "#519aba" }, "_makefile_light": { - "fontCharacter": "\\E05A", + "fontCharacter": "\\E05B", "fontColor": "#cc6d2e" }, "_makefile": { - "fontCharacter": "\\E05A", + "fontCharacter": "\\E05B", "fontColor": "#e37933" }, "_makefile_1_light": { - "fontCharacter": "\\E05A", + "fontCharacter": "\\E05B", "fontColor": "#9068b0" }, "_makefile_1": { - "fontCharacter": "\\E05A", + "fontCharacter": "\\E05B", "fontColor": "#a074c4" }, "_makefile_2_light": { - "fontCharacter": "\\E05A", + "fontCharacter": "\\E05B", "fontColor": "#627379" }, "_makefile_2": { - "fontCharacter": "\\E05A", + "fontCharacter": "\\E05B", "fontColor": "#6d8086" }, "_makefile_3_light": { - "fontCharacter": "\\E05A", + "fontCharacter": "\\E05B", "fontColor": "#498ba7" }, "_makefile_3": { - "fontCharacter": "\\E05A", + "fontCharacter": "\\E05B", "fontColor": "#519aba" }, "_markdown_light": { - "fontCharacter": "\\E05B", + "fontCharacter": "\\E05C", "fontColor": "#498ba7" }, "_markdown": { - "fontCharacter": "\\E05B", + "fontCharacter": "\\E05C", "fontColor": "#519aba" }, "_maven_light": { - "fontCharacter": "\\E05C", + "fontCharacter": "\\E05D", "fontColor": "#b8383d" }, "_maven": { - "fontCharacter": "\\E05C", + "fontCharacter": "\\E05D", "fontColor": "#cc3e44" }, "_mdo_light": { - "fontCharacter": "\\E05D", + "fontCharacter": "\\E05E", "fontColor": "#b8383d" }, "_mdo": { - "fontCharacter": "\\E05D", + "fontCharacter": "\\E05E", "fontColor": "#cc3e44" }, "_mustache_light": { - "fontCharacter": "\\E05E", + "fontCharacter": "\\E05F", "fontColor": "#cc6d2e" }, "_mustache": { - "fontCharacter": "\\E05E", + "fontCharacter": "\\E05F", "fontColor": "#e37933" }, + "_nim_light": { + "fontCharacter": "\\E061", + "fontColor": "#b7b73b" + }, + "_nim": { + "fontCharacter": "\\E061", + "fontColor": "#cbcb41" + }, "_npm_light": { - "fontCharacter": "\\E060", + "fontCharacter": "\\E062", "fontColor": "#3b4b52" }, "_npm": { - "fontCharacter": "\\E060", + "fontCharacter": "\\E062", "fontColor": "#41535b" }, "_npm_1_light": { - "fontCharacter": "\\E060", + "fontCharacter": "\\E062", "fontColor": "#b8383d" }, "_npm_1": { - "fontCharacter": "\\E060", + "fontCharacter": "\\E062", "fontColor": "#cc3e44" }, "_npm_ignored_light": { - "fontCharacter": "\\E061", + "fontCharacter": "\\E063", "fontColor": "#3b4b52" }, "_npm_ignored": { - "fontCharacter": "\\E061", + "fontCharacter": "\\E063", "fontColor": "#41535b" }, "_nunjucks_light": { - "fontCharacter": "\\E062", + "fontCharacter": "\\E064", "fontColor": "#7fae42" }, "_nunjucks": { - "fontCharacter": "\\E062", + "fontCharacter": "\\E064", "fontColor": "#8dc149" }, "_ocaml_light": { - "fontCharacter": "\\E063", + "fontCharacter": "\\E065", "fontColor": "#cc6d2e" }, "_ocaml": { - "fontCharacter": "\\E063", + "fontCharacter": "\\E065", "fontColor": "#e37933" }, "_odata_light": { - "fontCharacter": "\\E064", + "fontCharacter": "\\E066", "fontColor": "#cc6d2e" }, "_odata": { - "fontCharacter": "\\E064", + "fontCharacter": "\\E066", "fontColor": "#e37933" }, "_pddl_light": { - "fontCharacter": "\\E065", + "fontCharacter": "\\E067", "fontColor": "#9068b0" }, "_pddl": { - "fontCharacter": "\\E065", + "fontCharacter": "\\E067", "fontColor": "#a074c4" }, "_pdf_light": { - "fontCharacter": "\\E066", + "fontCharacter": "\\E068", "fontColor": "#b8383d" }, "_pdf": { - "fontCharacter": "\\E066", + "fontCharacter": "\\E068", "fontColor": "#cc3e44" }, "_perl_light": { - "fontCharacter": "\\E067", + "fontCharacter": "\\E069", "fontColor": "#498ba7" }, "_perl": { - "fontCharacter": "\\E067", + "fontCharacter": "\\E069", "fontColor": "#519aba" }, "_photoshop_light": { - "fontCharacter": "\\E068", + "fontCharacter": "\\E06A", "fontColor": "#498ba7" }, "_photoshop": { - "fontCharacter": "\\E068", + "fontCharacter": "\\E06A", "fontColor": "#519aba" }, "_php_light": { - "fontCharacter": "\\E069", + "fontCharacter": "\\E06B", "fontColor": "#9068b0" }, "_php": { - "fontCharacter": "\\E069", + "fontCharacter": "\\E06B", "fontColor": "#a074c4" }, "_plan_light": { - "fontCharacter": "\\E06A", + "fontCharacter": "\\E06C", "fontColor": "#7fae42" }, "_plan": { - "fontCharacter": "\\E06A", + "fontCharacter": "\\E06C", "fontColor": "#8dc149" }, "_platformio_light": { - "fontCharacter": "\\E06B", + "fontCharacter": "\\E06D", "fontColor": "#cc6d2e" }, "_platformio": { - "fontCharacter": "\\E06B", + "fontCharacter": "\\E06D", "fontColor": "#e37933" }, "_powershell_light": { - "fontCharacter": "\\E06C", + "fontCharacter": "\\E06E", "fontColor": "#498ba7" }, "_powershell": { - "fontCharacter": "\\E06C", + "fontCharacter": "\\E06E", "fontColor": "#519aba" }, "_prolog_light": { - "fontCharacter": "\\E06E", + "fontCharacter": "\\E070", "fontColor": "#cc6d2e" }, "_prolog": { - "fontCharacter": "\\E06E", + "fontCharacter": "\\E070", "fontColor": "#e37933" }, "_pug_light": { - "fontCharacter": "\\E06F", + "fontCharacter": "\\E071", "fontColor": "#b8383d" }, "_pug": { - "fontCharacter": "\\E06F", + "fontCharacter": "\\E071", "fontColor": "#cc3e44" }, "_puppet_light": { - "fontCharacter": "\\E070", + "fontCharacter": "\\E072", "fontColor": "#b7b73b" }, "_puppet": { - "fontCharacter": "\\E070", + "fontCharacter": "\\E072", "fontColor": "#cbcb41" }, "_python_light": { - "fontCharacter": "\\E071", + "fontCharacter": "\\E073", "fontColor": "#498ba7" }, "_python": { - "fontCharacter": "\\E071", + "fontCharacter": "\\E073", "fontColor": "#519aba" }, "_react_light": { - "fontCharacter": "\\E073", + "fontCharacter": "\\E075", "fontColor": "#498ba7" }, "_react": { - "fontCharacter": "\\E073", + "fontCharacter": "\\E075", "fontColor": "#519aba" }, "_react_1_light": { - "fontCharacter": "\\E073", + "fontCharacter": "\\E075", "fontColor": "#cc6d2e" }, "_react_1": { - "fontCharacter": "\\E073", + "fontCharacter": "\\E075", "fontColor": "#e37933" }, "_react_2_light": { - "fontCharacter": "\\E073", + "fontCharacter": "\\E075", "fontColor": "#b7b73b" }, "_react_2": { - "fontCharacter": "\\E073", + "fontCharacter": "\\E075", "fontColor": "#cbcb41" }, "_reasonml_light": { - "fontCharacter": "\\E074", + "fontCharacter": "\\E076", "fontColor": "#b8383d" }, "_reasonml": { - "fontCharacter": "\\E074", + "fontCharacter": "\\E076", "fontColor": "#cc3e44" }, "_rollup_light": { - "fontCharacter": "\\E075", + "fontCharacter": "\\E077", "fontColor": "#b8383d" }, "_rollup": { - "fontCharacter": "\\E075", + "fontCharacter": "\\E077", "fontColor": "#cc3e44" }, "_ruby_light": { - "fontCharacter": "\\E076", + "fontCharacter": "\\E078", "fontColor": "#b8383d" }, "_ruby": { - "fontCharacter": "\\E076", + "fontCharacter": "\\E078", "fontColor": "#cc3e44" }, "_rust_light": { - "fontCharacter": "\\E077", + "fontCharacter": "\\E079", "fontColor": "#627379" }, "_rust": { - "fontCharacter": "\\E077", + "fontCharacter": "\\E079", "fontColor": "#6d8086" }, "_salesforce_light": { - "fontCharacter": "\\E078", + "fontCharacter": "\\E07A", "fontColor": "#498ba7" }, "_salesforce": { - "fontCharacter": "\\E078", + "fontCharacter": "\\E07A", "fontColor": "#519aba" }, "_sass_light": { - "fontCharacter": "\\E079", + "fontCharacter": "\\E07B", "fontColor": "#dd4b78" }, "_sass": { - "fontCharacter": "\\E079", + "fontCharacter": "\\E07B", "fontColor": "#f55385" }, "_sbt_light": { - "fontCharacter": "\\E07A", + "fontCharacter": "\\E07C", "fontColor": "#498ba7" }, "_sbt": { - "fontCharacter": "\\E07A", + "fontCharacter": "\\E07C", "fontColor": "#519aba" }, "_scala_light": { - "fontCharacter": "\\E07B", + "fontCharacter": "\\E07D", "fontColor": "#b8383d" }, "_scala": { - "fontCharacter": "\\E07B", + "fontCharacter": "\\E07D", "fontColor": "#cc3e44" }, "_shell_light": { - "fontCharacter": "\\E07E", + "fontCharacter": "\\E080", "fontColor": "#455155" }, "_shell": { - "fontCharacter": "\\E07E", + "fontCharacter": "\\E080", "fontColor": "#4d5a5e" }, "_slim_light": { - "fontCharacter": "\\E07F", + "fontCharacter": "\\E081", "fontColor": "#cc6d2e" }, "_slim": { - "fontCharacter": "\\E07F", + "fontCharacter": "\\E081", "fontColor": "#e37933" }, "_smarty_light": { - "fontCharacter": "\\E080", + "fontCharacter": "\\E082", "fontColor": "#b7b73b" }, "_smarty": { - "fontCharacter": "\\E080", + "fontCharacter": "\\E082", "fontColor": "#cbcb41" }, "_spring_light": { - "fontCharacter": "\\E081", + "fontCharacter": "\\E083", "fontColor": "#7fae42" }, "_spring": { - "fontCharacter": "\\E081", + "fontCharacter": "\\E083", "fontColor": "#8dc149" }, "_stylelint_light": { - "fontCharacter": "\\E082", + "fontCharacter": "\\E084", "fontColor": "#bfc2c1" }, "_stylelint": { - "fontCharacter": "\\E082", + "fontCharacter": "\\E084", "fontColor": "#d4d7d6" }, "_stylelint_1_light": { - "fontCharacter": "\\E082", + "fontCharacter": "\\E084", "fontColor": "#455155" }, "_stylelint_1": { - "fontCharacter": "\\E082", + "fontCharacter": "\\E084", "fontColor": "#4d5a5e" }, "_stylus_light": { - "fontCharacter": "\\E083", + "fontCharacter": "\\E085", "fontColor": "#7fae42" }, "_stylus": { - "fontCharacter": "\\E083", + "fontCharacter": "\\E085", "fontColor": "#8dc149" }, "_sublime_light": { - "fontCharacter": "\\E084", + "fontCharacter": "\\E086", "fontColor": "#cc6d2e" }, "_sublime": { - "fontCharacter": "\\E084", + "fontCharacter": "\\E086", "fontColor": "#e37933" }, "_svg_light": { - "fontCharacter": "\\E085", + "fontCharacter": "\\E087", "fontColor": "#9068b0" }, "_svg": { - "fontCharacter": "\\E085", + "fontCharacter": "\\E087", "fontColor": "#a074c4" }, "_svg_1_light": { - "fontCharacter": "\\E085", + "fontCharacter": "\\E087", "fontColor": "#498ba7" }, "_svg_1": { - "fontCharacter": "\\E085", + "fontCharacter": "\\E087", "fontColor": "#519aba" }, "_swift_light": { - "fontCharacter": "\\E086", + "fontCharacter": "\\E088", "fontColor": "#cc6d2e" }, "_swift": { - "fontCharacter": "\\E086", + "fontCharacter": "\\E088", "fontColor": "#e37933" }, "_terraform_light": { - "fontCharacter": "\\E087", + "fontCharacter": "\\E089", "fontColor": "#9068b0" }, "_terraform": { - "fontCharacter": "\\E087", + "fontCharacter": "\\E089", "fontColor": "#a074c4" }, "_tex_light": { - "fontCharacter": "\\E088", + "fontCharacter": "\\E08A", "fontColor": "#498ba7" }, "_tex": { - "fontCharacter": "\\E088", + "fontCharacter": "\\E08A", "fontColor": "#519aba" }, "_tex_1_light": { - "fontCharacter": "\\E088", + "fontCharacter": "\\E08A", "fontColor": "#b7b73b" }, "_tex_1": { - "fontCharacter": "\\E088", + "fontCharacter": "\\E08A", "fontColor": "#cbcb41" }, "_tex_2_light": { - "fontCharacter": "\\E088", + "fontCharacter": "\\E08A", "fontColor": "#cc6d2e" }, "_tex_2": { - "fontCharacter": "\\E088", + "fontCharacter": "\\E08A", "fontColor": "#e37933" }, "_tex_3_light": { - "fontCharacter": "\\E088", + "fontCharacter": "\\E08A", "fontColor": "#bfc2c1" }, "_tex_3": { - "fontCharacter": "\\E088", + "fontCharacter": "\\E08A", "fontColor": "#d4d7d6" }, "_todo": { - "fontCharacter": "\\E08A" + "fontCharacter": "\\E08C" }, "_tsconfig_light": { - "fontCharacter": "\\E08B", + "fontCharacter": "\\E08D", "fontColor": "#498ba7" }, "_tsconfig": { - "fontCharacter": "\\E08B", + "fontCharacter": "\\E08D", "fontColor": "#519aba" }, "_twig_light": { - "fontCharacter": "\\E08C", + "fontCharacter": "\\E08E", "fontColor": "#7fae42" }, "_twig": { - "fontCharacter": "\\E08C", + "fontCharacter": "\\E08E", "fontColor": "#8dc149" }, "_typescript_light": { - "fontCharacter": "\\E08D", + "fontCharacter": "\\E08F", "fontColor": "#498ba7" }, "_typescript": { - "fontCharacter": "\\E08D", + "fontCharacter": "\\E08F", "fontColor": "#519aba" }, "_typescript_1_light": { - "fontCharacter": "\\E08D", + "fontCharacter": "\\E08F", "fontColor": "#b7b73b" }, "_typescript_1": { - "fontCharacter": "\\E08D", + "fontCharacter": "\\E08F", "fontColor": "#cbcb41" }, "_vala_light": { - "fontCharacter": "\\E08E", + "fontCharacter": "\\E090", "fontColor": "#627379" }, "_vala": { - "fontCharacter": "\\E08E", + "fontCharacter": "\\E090", "fontColor": "#6d8086" }, "_video_light": { - "fontCharacter": "\\E08F", + "fontCharacter": "\\E091", "fontColor": "#dd4b78" }, "_video": { - "fontCharacter": "\\E08F", + "fontCharacter": "\\E091", "fontColor": "#f55385" }, "_vue_light": { - "fontCharacter": "\\E090", + "fontCharacter": "\\E092", "fontColor": "#7fae42" }, "_vue": { - "fontCharacter": "\\E090", + "fontCharacter": "\\E092", "fontColor": "#8dc149" }, "_wasm_light": { - "fontCharacter": "\\E091", + "fontCharacter": "\\E093", "fontColor": "#9068b0" }, "_wasm": { - "fontCharacter": "\\E091", + "fontCharacter": "\\E093", "fontColor": "#a074c4" }, "_wat_light": { - "fontCharacter": "\\E092", + "fontCharacter": "\\E094", "fontColor": "#9068b0" }, "_wat": { - "fontCharacter": "\\E092", + "fontCharacter": "\\E094", "fontColor": "#a074c4" }, "_webpack_light": { - "fontCharacter": "\\E093", + "fontCharacter": "\\E095", "fontColor": "#498ba7" }, "_webpack": { - "fontCharacter": "\\E093", + "fontCharacter": "\\E095", "fontColor": "#519aba" }, "_wgt_light": { - "fontCharacter": "\\E094", + "fontCharacter": "\\E096", "fontColor": "#498ba7" }, "_wgt": { - "fontCharacter": "\\E094", + "fontCharacter": "\\E096", "fontColor": "#519aba" }, "_windows_light": { - "fontCharacter": "\\E095", + "fontCharacter": "\\E097", "fontColor": "#498ba7" }, "_windows": { - "fontCharacter": "\\E095", + "fontCharacter": "\\E097", "fontColor": "#519aba" }, "_word_light": { - "fontCharacter": "\\E096", + "fontCharacter": "\\E098", "fontColor": "#498ba7" }, "_word": { - "fontCharacter": "\\E096", + "fontCharacter": "\\E098", "fontColor": "#519aba" }, "_xls_light": { - "fontCharacter": "\\E097", + "fontCharacter": "\\E099", "fontColor": "#7fae42" }, "_xls": { - "fontCharacter": "\\E097", + "fontCharacter": "\\E099", "fontColor": "#8dc149" }, "_xml_light": { - "fontCharacter": "\\E098", + "fontCharacter": "\\E09A", "fontColor": "#cc6d2e" }, "_xml": { - "fontCharacter": "\\E098", + "fontCharacter": "\\E09A", "fontColor": "#e37933" }, "_yarn_light": { - "fontCharacter": "\\E099", + "fontCharacter": "\\E09B", "fontColor": "#498ba7" }, "_yarn": { - "fontCharacter": "\\E099", + "fontCharacter": "\\E09B", "fontColor": "#519aba" }, "_yml_light": { - "fontCharacter": "\\E09A", + "fontCharacter": "\\E09C", "fontColor": "#9068b0" }, "_yml": { - "fontCharacter": "\\E09A", + "fontCharacter": "\\E09C", "fontColor": "#a074c4" }, "_zip_light": { - "fontCharacter": "\\E09B", + "fontCharacter": "\\E09D", "fontColor": "#b8383d" }, "_zip": { - "fontCharacter": "\\E09B", + "fontCharacter": "\\E09D", "fontColor": "#cc3e44" }, "_zip_1_light": { - "fontCharacter": "\\E09B", + "fontCharacter": "\\E09D", "fontColor": "#627379" }, "_zip_1": { - "fontCharacter": "\\E09B", + "fontCharacter": "\\E09D", "fontColor": "#6d8086" } }, @@ -1384,6 +1408,7 @@ "hh": "_cpp_1", "hpp": "_cpp_1", "hxx": "_cpp_1", + "h++": "_cpp_1", "edn": "_clojure_1", "cfc": "_coldfusion", "cfm": "_coldfusion", @@ -1400,6 +1425,9 @@ "csv": "_csv", "xls": "_xls", "xlsx": "_xls", + "cu": "_cu", + "cuh": "_cu_1", + "hu": "_cu_1", "cake": "_cake", "ctp": "_cake_php", "d": "_d", @@ -1428,6 +1456,7 @@ "hxs": "_haxe_1", "hxp": "_haxe_2", "hxml": "_haxe_3", + "jade": "_jade", "class": "_java", "classpath": "_java", "properties": "_java", @@ -1449,6 +1478,8 @@ "ad": "_argdown", "mustache": "_mustache", "stache": "_mustache", + "nim": "_nim", + "nims": "_nim", "njk": "_nunjucks", "nunjucks": "_nunjucks", "nunjs": "_nunjucks", @@ -1456,8 +1487,6 @@ "njs": "_nunjucks", "nj": "_nunjucks", "npm-debug.log": "_npm", - "npmignore": "_npm_1", - "npmrc": "_npm_1", "ml": "_ocaml", "mli": "_ocaml", "cmx": "_ocaml", @@ -1467,7 +1496,6 @@ "pddl": "_pddl", "plan": "_plan", "happenings": "_happenings", - "pug": "_pug", "pp": "_puppet", "epp": "_puppet", "spec.jsx": "_react_1", @@ -1485,6 +1513,7 @@ "springbeans": "_spring", "slim": "_slim", "smarty.tpl": "_smarty", + "tpl": "_smarty", "sbt": "_sbt", "scala": "_scala", "sol": "_ethereum", @@ -1523,6 +1552,8 @@ "pxm": "_image", "svg": "_svg", "svgx": "_image", + "tiff": "_image", + "webp": "_image", "sublime-project": "_sublime", "sublime-workspace": "_sublime", "component": "_salesforce", @@ -1642,11 +1673,13 @@ "csharp": "_c-sharp", "css": "_css", "dockerfile": "_docker", + "ignore": "_npm_1", "fsharp": "_f-sharp", "go": "_go2", "groovy": "_grails", "handlebars": "_mustache", "html": "_html_3", + "properties": "_npm_1", "java": "_java", "javascriptreact": "_react", "javascript": "_javascript", @@ -1660,7 +1693,7 @@ "perl": "_perl", "php": "_php", "powershell": "_powershell", - "jade": "_jade", + "jade": "_pug", "python": "_python", "r": "_R", "razor": "_html", @@ -1712,6 +1745,7 @@ "hh": "_cpp_1_light", "hpp": "_cpp_1_light", "hxx": "_cpp_1_light", + "h++": "_cpp_1_light", "edn": "_clojure_1_light", "cfc": "_coldfusion_light", "cfm": "_coldfusion_light", @@ -1728,6 +1762,9 @@ "csv": "_csv_light", "xls": "_xls_light", "xlsx": "_xls_light", + "cu": "_cu_light", + "cuh": "_cu_1_light", + "hu": "_cu_1_light", "cake": "_cake_light", "ctp": "_cake_php_light", "d": "_d_light", @@ -1756,6 +1793,7 @@ "hxs": "_haxe_1_light", "hxp": "_haxe_2_light", "hxml": "_haxe_3_light", + "jade": "_jade_light", "class": "_java_light", "classpath": "_java_light", "properties": "_java_light", @@ -1777,6 +1815,8 @@ "ad": "_argdown_light", "mustache": "_mustache_light", "stache": "_mustache_light", + "nim": "_nim_light", + "nims": "_nim_light", "njk": "_nunjucks_light", "nunjucks": "_nunjucks_light", "nunjs": "_nunjucks_light", @@ -1784,8 +1824,6 @@ "njs": "_nunjucks_light", "nj": "_nunjucks_light", "npm-debug.log": "_npm_light", - "npmignore": "_npm_1_light", - "npmrc": "_npm_1_light", "ml": "_ocaml_light", "mli": "_ocaml_light", "cmx": "_ocaml_light", @@ -1795,7 +1833,6 @@ "pddl": "_pddl_light", "plan": "_plan_light", "happenings": "_happenings_light", - "pug": "_pug_light", "pp": "_puppet_light", "epp": "_puppet_light", "spec.jsx": "_react_1_light", @@ -1813,6 +1850,7 @@ "springbeans": "_spring_light", "slim": "_slim_light", "smarty.tpl": "_smarty_light", + "tpl": "_smarty_light", "sbt": "_sbt_light", "scala": "_scala_light", "sol": "_ethereum_light", @@ -1851,6 +1889,8 @@ "pxm": "_image_light", "svg": "_svg_light", "svgx": "_image_light", + "tiff": "_image_light", + "webp": "_image_light", "sublime-project": "_sublime_light", "sublime-workspace": "_sublime_light", "component": "_salesforce_light", @@ -1913,11 +1953,13 @@ "csharp": "_c-sharp_light", "css": "_css_light", "dockerfile": "_docker_light", + "ignore": "_npm_1_light", "fsharp": "_f-sharp_light", "go": "_go2_light", "groovy": "_grails_light", "handlebars": "_mustache_light", "html": "_html_3_light", + "properties": "_npm_1_light", "java": "_java_light", "javascriptreact": "_react_light", "javascript": "_javascript_light", @@ -1931,7 +1973,7 @@ "perl": "_perl_light", "php": "_php_light", "powershell": "_powershell_light", - "jade": "_jade_light", + "jade": "_pug_light", "python": "_python_light", "r": "_R_light", "razor": "_html_light", @@ -2024,5 +2066,5 @@ "npm-debug.log": "_npm_ignored_light" } }, - "version": "https://github.com/jesseweed/seti-ui/commit/f3b2775662b0075aab56e5f0c03269f21f3f0f30" + "version": "https://github.com/jesseweed/seti-ui/commit/719e5d384e878b0e190abc80247a8726f083a393" } \ No newline at end of file From 40cb0f3433de79be3bc63eef6430c957884c8ae9 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 31 Aug 2020 22:26:53 +0200 Subject: [PATCH 736/736] Increase initial connection timeout --- .../platform/remote/common/remoteAgentConnection.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/remote/common/remoteAgentConnection.ts b/src/vs/platform/remote/common/remoteAgentConnection.ts index b8b6b2c35a9..18d3d04fd20 100644 --- a/src/vs/platform/remote/common/remoteAgentConnection.ts +++ b/src/vs/platform/remote/common/remoteAgentConnection.ts @@ -16,6 +16,9 @@ import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async import { ILogService } from 'vs/platform/log/common/log'; import { IIPCLogger } from 'vs/base/parts/ipc/common/ipc'; +const INITIAL_CONNECT_TIMEOUT = 120 * 1000 /* 120s */; +const RECONNECT_TIMEOUT = 30 * 1000 /* 30s */; + export const enum ConnectionType { Management = 1, ExtensionHost = 2, @@ -277,7 +280,7 @@ export async function connectRemoteAgentManagement(options: IConnectionOptions, try { const reconnectionToken = generateUuid(); const simpleOptions = await resolveConnectionOptions(options, reconnectionToken, null); - const { protocol } = await connectWithTimeLimit(simpleOptions.logService, doConnectRemoteAgentManagement(simpleOptions), 30 * 1000 /*30s*/); + const { protocol } = await connectWithTimeLimit(simpleOptions.logService, doConnectRemoteAgentManagement(simpleOptions), INITIAL_CONNECT_TIMEOUT); return new ManagementPersistentConnection(options, remoteAuthority, clientId, reconnectionToken, protocol); } catch (err) { options.logService.error(`[remote-connection] An error occurred in the very first connect attempt, it will be treated as a permanent error! Error:`); @@ -291,7 +294,7 @@ export async function connectRemoteAgentExtensionHost(options: IConnectionOption try { const reconnectionToken = generateUuid(); const simpleOptions = await resolveConnectionOptions(options, reconnectionToken, null); - const { protocol, debugPort } = await connectWithTimeLimit(simpleOptions.logService, doConnectRemoteAgentExtensionHost(simpleOptions, startArguments), 30 * 1000 /*30s*/); + const { protocol, debugPort } = await connectWithTimeLimit(simpleOptions.logService, doConnectRemoteAgentExtensionHost(simpleOptions, startArguments), INITIAL_CONNECT_TIMEOUT); return new ExtensionHostPersistentConnection(options, startArguments, reconnectionToken, protocol, debugPort); } catch (err) { options.logService.error(`[remote-connection] An error occurred in the very first connect attempt, it will be treated as a permanent error! Error:`); @@ -303,7 +306,7 @@ export async function connectRemoteAgentExtensionHost(options: IConnectionOption export async function connectRemoteAgentTunnel(options: IConnectionOptions, tunnelRemotePort: number): Promise { const simpleOptions = await resolveConnectionOptions(options, generateUuid(), null); - const protocol = await connectWithTimeLimit(simpleOptions.logService, doConnectRemoteAgentTunnel(simpleOptions, { port: tunnelRemotePort }), 30 * 1000 /*30s*/); + const protocol = await connectWithTimeLimit(simpleOptions.logService, doConnectRemoteAgentTunnel(simpleOptions, { port: tunnelRemotePort }), INITIAL_CONNECT_TIMEOUT); return protocol; } @@ -434,7 +437,7 @@ abstract class PersistentConnection extends Disposable { this._options.logService.info(`${logPrefix} resolving connection...`); const simpleOptions = await resolveConnectionOptions(this._options, this.reconnectionToken, this.protocol); this._options.logService.info(`${logPrefix} connecting to ${simpleOptions.host}:${simpleOptions.port}...`); - await connectWithTimeLimit(simpleOptions.logService, this._reconnect(simpleOptions), 30 * 1000 /*30s*/); + await connectWithTimeLimit(simpleOptions.logService, this._reconnect(simpleOptions), RECONNECT_TIMEOUT); this._options.logService.info(`${logPrefix} reconnected!`); this._onDidStateChange.fire(new ConnectionGainEvent());